C++左右值和完美转发
常用例子
int& GetValue(){
static int val = 10;
return val;
}
int i = GetValue(); //当然,返回 int 而不是 int&也可以这样用
GetValue() = 5;
/*函数不应该返回在栈上生成的变量的引用*/void SetValue(int v){
//none
}
int i = 10;
SetValue(i);
SetValue(10);void SetValue(int& v){
//none
}
int i = 10;
SetValue(i);
SetValue(10); //errorvoid SetValue(const int& v){
//none
}
int i = 10;
SetValue(i);
SetValue(10); //okint& a = 10;//error
//引用 所引用的东西,必须是一个左值
/*
int &ri = 3;
ri = 4; // error! 3 = 4
*/
const int& a = 10;//okvoid speak(string& name) {
cout << name;
return;
}
int main() {
string s1 = "xxx";
string s2 = "yyy";
cout << s1 + s2;
speak(s1 + s2); //error : 非常量引用 的初始值 必须为左值
return 0;
}
//如果 void speak(const string& name);void speak_r(string&& name) {
cout << name;
return;
}
speak_r(s1 + s2); //ok
speak_r(s1); //no
//右值引用左值:持续存储的变量 右值:临时的结果
speak_r 函数的意义在于,我们知道传入的是一个临时的东西,所以我们不在乎破坏它,偷窃它内部的东西(如果是类对象)
函数返回非基本类型
当你想要写一个函数,返回一个有内容的 vector
1、写一个返回值是 vector 的函数
vector<int> Get(){
vector<int> temp;
// fill in values
return temp;
}
vector<int> vec = Get();这个看起来的问题是,一开始在栈上构建了 temp,但是不同于 Java,要把 temp 整个复制到函数外的 vec 里
而 vector 重载了=号,并且是真拷贝(另一个内容了)
2、再 Get() 里,通过 new 得到一个新的 vector,然后返回其指针。
但是这个的问题是,需要外部函数来管理内部函数创建的内部,对于 C++来说,不是好的处理方式
3、一开始创建好 vec,函数形参是 vector 的引用。
(标准库里的 string,vector 似乎会自动优化,即使写成了 1 也没那么多拷贝,这个叫做返回值优化,大多编译器都会给你做好,但为了理解我们先不管)
但你从一个函数中创建对象,然后返回它,这就要拷贝了。尤其是你的对象,创建时需要在堆上分配内存。
简单总结
无论是函数的形参还是返回值,只要是对象类型(非引用、指针),就会发生拷贝
右值引用与 std::move
const int& a = 8;
int&& b = 10;
b++;
std::cout << b;右值引用,为右值提供了一个地址,并且之后可以修改它
void foo(Resource &lrr);
void foo(Resource &&rrr);
foo(Resource());#include <iostream>
class Resource {
public:
Resource() {
std::cout << "Constructing..." << std::endl;
}
Resource(const Resource& r) {
std::cout << "Copy Constructing..." << std::endl;
}
};
void addResource(Resource &&rrr) {
Resource r = rrr;
}
int main(int argc, char *argv[])
{
addResource(Resource());
}Constructing... Copy Constructing...
1、Resource() 的返回值是一个右值
2、addResource(Resource &&rrr) 需求一个右值,因此在传参时,是没有问题的
3、问题发生在 Resource r = rrr;
rrr 实际上是一个右值引用,而在 Resource r = rrr; 里变成了一个左值
因此,Resource r = rrr; 会调用复制构造函数
这和前面那个例子是一样的
void speak_r(string&& name) {
cout << name;
return;
}
speak_r(s1 + s2); //ok
speak_r(s1); //no所以此处要使用 std::move
Resource r = std::move(rrr);并且新增一个右值引用构造函数
move
rval = std::move(lval) // std::move { static_cast<T&&>(lvalue) }- 单纯的
move不会造成所有权的转移 - 单纯的
move不会造成生命周期缩短
这两个效果都是通过右值引用参数函数来实现的
Array(Array&& temp_array) {
data_ = temp_array.data_; // 所有权转移
size_ = temp_array.size_;
temp_array.data_ = nullptr; // 生命周期缩短
}完美转发
完美转发大概就是说,一个函数经过重载,一个处理传递的实参是左值,一个处理传递的实参是右值
额外
void reference(int& v) {
std::cout << "called left value" << std::endl;
}
void reference(int&& v) {
std::cout << "called right value" << std::endl;
}
void pass(int&& v) {
reference(v); // 始终调用 reference(int&)
}
int main()
{
pass(1);
return 0;
}输出 left
函数形参虽然是右值,但是内部传到另一个函数时,又被理解为左值