右值引用(Rvalue Reference)是C++11的新特性,它实现了 转移语义(move) 和 完美转发(perfect forwarding),主要目的分别是:
- 减少两个对象交互时不必要的拷贝构造,提升效率
- 简洁明确定义泛型函数
C++中的变量要么是左值、要么是右值。通俗的左值定义指的是⾮临时变量,⽽右值指的是临时对 象。左值引⽤的符号是⼀个&,右值引⽤是两个&&
绑定到右值的引用,用&&
来指定。右值引用只能绑定到要销毁的对象。
int var = 42;
int &l_var = var;
int &&r_var = var; // 错误:不能将右值引⽤绑定到左值上
int &&r_var2 = var + 40; // 正确:将 r_var2 绑定到求和结果上
转移语义
借此可以实现转移语义,即从右值中直接拿数据过来初始化或修改左值, ⽽不需要重新构造一个临时左值后再析构右值。简单来说解决的是各种情形下对象的资源所有权转移的问题。
转移语义可以解决以下问题:
① 用右值参数
// 在实现转移语义之前
MyString a = new MyString(b + "dafs"); // 成功,const &的形参接收了右值参数,但是调用了额外的拷贝构造函数,生成了一个临时变量,销毁临时变量还调用了一次析构函数
// 实现转移语义之后
MyString a = new MyString(b + "dafs"); // 成功,会调用1次移动构造函数
上述提到移动构造函数格式如下:
MyString(MyString && str)
{
_len = str._len;
_data = str._data; // 从右值中获取数据
str._len = 0;
str._data = NULL; // 销毁右值
}
顺便说一下,C++内置的函数std::move()
可以将一个左值变量转为右值。(后面会详细介绍)
在实现移动构造函数之前,MyString a = move(b)
只会调用拷贝构造函数(如果实现了的话,否则只是复制成员变量)。 而实现了移动构造函数之后,会调用移动构造函数(move(b)被当做右值)。
② 按值返回
在有移动语义之前,当一个函数需要返回一个容器对象时,为了不触发额外的拷贝构造,通常会这样写:
void str_split(const string& s, vector<string>* vec); // 一个按值语义定义的字符串拆分函数
这样vec就必须在外部被定义,很不方便。
而有了移动语义就可以这样:
vector<string> str_split(const string& s) {
vector<string> v;
// ...
return v