C++11的右值引用

151 阅读3分钟

什么是右值,什么是左值

我们写一个赋值表达式,能放在等于号的左边就是左值,在右边就是右值,或者说不能取地址的叫做右值。当然也不是完全正确。 一个例外的情况是const 修饰的常量,我们认为是右值。

几个判断标准

  • 普通类型的变量,可以取地址认为是左值
  • const修饰的常量,不可修改,只读类型的,理论应该按照右值对待,但因为其可以取地址(如果只是const类型常量的定义,编译器不给其开辟空间,如果对该常量取地址时,编译器才为其开辟空间),C++11认为其是左值
  • 表达式的运行结果是一个临时变量或对象,认为是右值
  • 变量是一个引用认为是左值

右值引用

int main()

{

// 10纯右值,本来只是一个符号,没有具体的空间,

// 右值引用变量r1在定义过程中,编译器产生了一个临时变量,r1实际引用的是临时变量

int&& r1 = 10;

r1 = 100;

int a = 10;

int&& r2 = a; // 编译失败:右值引用不能引用左值

return 0;
}

const 引用的变量,左值引用右值引用都可以

右值引用的作用

值返回的缺陷

对于一个函数,返回一个值,当出了函数作用域的时候,这个值会被销毁。所以要拷贝构造这个值,然后再把这个值再拷贝构造我们需要的值。这时也就是产生了三个资源一模一样的对象。对于空间来说是一种浪费。程序的效率也会降低。

移动语义

我们引入移动语义来解决上述的问题,就是将一个对象的资源移动到另一个对象的方式。 函数的返回值被认为是将亡值,也就是右值,它把这个对象的资源转移到临时对象中,临时对象中的资源又转移到我们要的对象中。整个过程中,只需要创建一块堆内存就可以了,节省空间提升效率。

但是实现移动语义要又移动构造函数,而且移动构造函数的参数不能是const类型的右值引用。

完美转发

模板中的&& 不在是右值引用,而是万能引用。既能接收左值,又能接收右值。 但是对于函数模板内部来说,形参既有名称,又能寻址,就被识别为了左值,那么如何才能将函数模板接收到的形参连同其左、右值属性,一起传递给被调用的函数呢?

为此我们引入模板函数 forword()


#include <iostream>
using namespace std;

//重载被调用函数,查看完美转发的效果
void otherdef(int & t) {
    cout << "lvalue\n";
}
void otherdef(const int & t) {
    cout << "rvalue\n";
}

//实现完美转发的函数模板
template <typename T>
void function(T&& t) {
    otherdef(forward<T>(t));
}

int main()
{
    function(5);
    int  x = 1;
    function(x);
    return 0;
}

程序执行结果为 rvalue
lvalue 综上:完美转发就是forword() 函数模板用于修饰被调用函数中需要维持参数左、右值属性的参数。