来源: http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html
本文来自Alex Allain的文章,对文中大概意思做一个翻译。
C++是用来产生更快的代码,但是C++中的临时对象降低了C++的性能。某些临时对象可以被编译器优化(比如,函数返回值优化),但是有时候临时对象会造成严重的对象拷贝(比如deep copy)。
举例如下:
#include <iostream>
using namespace std;
vector<int> doubleValues (const vector<int>& v)
{
vector<int> new_values;
new_values.reserve();
for (auto itr = v.begin(), end_itr = v.end(); itr != end_itr; ++itr )
{
new_values.push_back( 2 * *itr );
}
return new_values;
}
int main()
{
vector<int> v;
for ( int i = 0; i < 100; i++ )
{
v.push_back( i );
}
v = doubleValues( v );
}
其中,v = doubleValues(v),发生了两次拷贝。第一次在doubleValues()中,函数返回值是一个临时对象,需要将new_values拷贝到这个临时对象中(调用copy constructor),这次拷贝可能会被编译器自动优化。第二次拷贝是将临时对象的值赋给v(调用了assignment function),这需要重新分配空间并且遍历整个vector进行赋值。
虽然我们有其他的办法来避免这种情况,比如通过返回指针或者传递引用的方式。但是这些方式都不是自然。这段代码最坏的地方是从 doubleValues()返回的对象是一个临时对象,而这个临时对象是很快就不再需要了。一旦把临时对象的值 copy 到 v 中,这个临时对象就不再需要了。但是直观来看,最简单的办法是通过将临时对象中的指针保存到 v 中,而跳过整个copy过程。这就是所谓的 move(转移)。但是在C++03中这个功能并不能实现,因为你无法通过赋值函数,或者copy ctor判断传递其的对象是否是临时对象(temporary object),从而也就无法实现move功能了。但是在C++11中,你可以做到!
这就是move语义和右值引用的目的。move 语义允许你通过将一个临时对象中的资源安全的转移到另外一个对象中,避免了资源浪费。
move 语义依赖C++11中的新特性:右值引用。我们首先了解什么是右值,然后在了解什么是右值引用,最后我们来理解这到底是如何工作的。
Rvalues and Lvalues (右值和左值)
简单来说,左值是可以出现在赋值号左边的表达式,右值是出现在赋值号右边的表达式。左值有分配空间,我们可以给其赋值。
左值不一定是变量。
int x; // 全局变量
int& getRef ()
{
return x;
}
getRef() = 4; // 返回全局变量的引用。 getRef()也是左值。
右值-----如果一个表达式返回一个临时对象,那么这个表达式是右值。例如:
int x; // x 是全局变量
int getVal ()
{
return x; // 返回值是值传递
}
getVal(); // 返回临时变量,所以这里getVal()是右值。
当函数返回值类型是对象而不是int类型时:
string getName ()
{
return "Alex";
}
getName(); //返回一个string 类型的临时变量,也是右值。
string str = getName();
最关键的事情是要记住 “右值指的是临时对象”!!!
通过"右值引用" 来 引用临时对象
在C++03中,可以采用如下方式引用一个临时对象,但是这个引用必须是const。
const string& name = getName(); // ok
string& name = getName(); // NOT ok
这里,只能通过const 引用一个临时对象,所以你不能对一个临时对象进行操作。
在C++11中,允许你使用“rvalue reference”来引用一个临时对象,并且这个引用可以是非const的。
const string&& name = getName(); // ok
string&& name = getName(); // also ok - praise be!
1) rvalue reference 只能引用临时对象。
2) rvalue reference 可以是const 也可以是非const。只是非const很少使用
3) rvalue reference 使用&& 语法表示引用临时对象。
右值引用最重要的作用是,当右值引用和左值引用分别作为函数的参数时,进行函数重载,会发生尤其有意思的事情。
printReference (const String& str) //#1
{
cout << str;
}
printReference (String&& str) //#2
{
cout << str;
}
#1的参数是一个左值引用,它会接受所有的参数,不管这个参数是左值还是右值,无论这个参数是可变的还是不可变的。
#2的参数是一个右值引用,它指接受可变的右值(也就是可变的临时对象)。
举例如下:
string me( "alex" );
printReference( me ); // 调用 #1
printReference( getName() ); // 调用#2 作为可变的右值引用
那我们如何通过这个方法来使用临时对象呢? 或者说我们如何利用这个方法呢?(目的是避免深度copy,提升程序效率)
Move constructor and move assignment operator
(未完待续。。。)