官方定义是,可以直接取得到地址的对象就是左值,而不能取地址的对象就是右值。但按我的理解来说,如果这个对象是有名字(变量名)的,那就是左值;而除常量数组之外,如果没有名字的(比如临时对象),就是右值,同时也称“将亡值”。
// 左值
int a; // 变量
vector buf; // 对象
"sss"; // 字符串常量
// 右值
10;
string(); vector(); // 匿名对象
右值引用就是给右值(将亡值)取别名,然后当我们用将亡值来进行拷贝或赋值时可以把 this 对象和将亡值的内容进行交换,以此减少拷贝次数,增加拷贝效率。
一般的左值引用确实不能引用右值,但 const 的左值引用既可以引用左值,也可以引用右值;然而,右值引用只能引用右值,但其实也可以通过 move() 函数引用左值。
移动语义其实就是把两个对象之间的内容进行交换,来减少拷贝次数的原理。
可以看成是一个把左值传进去,然后返回右值的函数。
template
void func(T&& t) // 注:"T&&" 是万能引用,不是右值引用
{
Func(t);
}
万能引用既能接受左值,也能接受右值。当且仅当 func() 是函数模板时,T&& 表示的就是万能引用,不是右值引用。当实参是左值时,&& 就会自动折叠成 &(俗称“引用折叠”),当实参是右值时,&& 不会进行折叠。
首先,我们可以跑一跑下面的代码:
void Fun(int& x) { cout << "lvalue ref" << endl; }
void Fun(int&& x) { cout << "rvalue ref" << endl; }
void Fun(const int& x) { cout << "const lvalue ref" << endl; }
void Fun(const int&& x) { cout << "const rvalue ref" << endl; }
template
void PerfectForward(T&& t)
{
Fun(t);
}
int main()
{
PerfectForward(10);
int a;
PerfectForward(a);
PerfectForward(std::move(a));
const int b = 8;
PerfectForward(b);
PerfectForward(std::move(b));
return 0;
}
如果结果都是左值引用,别感到奇怪,因为在 PerfectForward() 函数里面, 无论实参是右值还是左值, 进来 PerfectForward() 后都会有一个变量名——t。而当这个对象有了变量名,这个对象在这个函数作用域里就是左值了,即使它原本是右值。因此就会导致右值一旦进入一个函数就会悄悄地变成左值,而这是不好的。因此,为了保持对象在函数传参时左右值属性不变,C++便引入了完美转发。 或者也可以这么说,完美转发一般都是与万能引用搭配使用的。
完美转发的作用就是保持函数形参的左右值属性。
以上面的代码为例,如果 t 原本是右值传进来的,那么经过完美转发后,t 还会是右值;而如果 t 原本是左值传进来的,那么通过完美转发后 t 还会是左值。
C++的完美转发就是通过 std::forward