C++进阶——C++11(右值引用)

一、右值 VS 左值

官方定义是,可以直接取得到地址的对象就是左值,而不能取地址的对象就是右值。但按我的理解来说,如果这个对象是有名字(变量名)的,那就是左值;而除常量数组之外,如果没有名字的(比如临时对象),就是右值,同时也称“将亡值”。

// 左值
int a; // 变量
vector buf; // 对象
"sss"; // 字符串常量

// 右值
10; 
string(); vector(); // 匿名对象

二、右值引用(Type&&)

右值引用就是给右值(将亡值)取别名,然后当我们用将亡值来进行拷贝或赋值时可以把 this 对象和将亡值的内容进行交换,以此减少拷贝次数,增加拷贝效率。

三、右值引用 VS 左值引用

一般的左值引用确实不能引用右值,但 const 的左值引用既可以引用左值,也可以引用右值;然而,右值引用只能引用右值,但其实也可以通过 move() 函数引用左值。

四、移动语义

1、什么是移动语义

移动语义其实就是把两个对象之间的内容进行交换,来减少拷贝次数的原理。

C++进阶——C++11(右值引用)_第1张图片

2、move() 函数

可以看成是一个把左值传进去,然后返回右值的函数。

五、完美转发

1、万能引用

template
void func(T&& t) // 注:"T&&" 是万能引用,不是右值引用
{ 
    Func(t); 
}

万能引用既能接受左值,也能接受右值。当且仅当 func() 是函数模板时,T&& 表示的就是万能引用,不是右值引用。当实参是左值时,&& 就会自动折叠成 &(俗称“引用折叠”),当实参是右值时,&& 不会进行折叠。 

2、为什么要完美转发

(1)C++函数形参属性转换规律:右值->左值

首先,我们可以跑一跑下面的代码:

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++便引入了完美转发。 或者也可以这么说,完美转发一般都是与万能引用搭配使用的。

(2)完美转发的作用

完美转发的作用就是保持函数形参的左右值属性。

以上面的代码为例,如果 t 原本是右值传进来的,那么经过完美转发后,t 还会是右值;而如果 t 原本是左值传进来的,那么通过完美转发后 t 还会是左值。

3、std::forward()

C++的完美转发就是通过 std::forward() 函数来实现的。

你可能感兴趣的:(c++,开发语言)