本文翻译自关于右值引用解释的经典文章,如果英文还可以的话,直接去看英文原文。thbecker.net/articles/rvalue_references/section_01.html
右值引用是c++中的一个特性,并且已经入驻c++11标准,可能大家一开始接触的时候感觉有点难以理解,但是他的确是很好用的一个玩意儿。
右值引用解决了两个问题:
1.move语义 2.完美转发。
我们接下来先简单介绍一下move语义,但是介绍move语义之前,我们需要先介绍左值和右值的含义。
在C语言中,我们给出左值的定义为:左值是一个表达式e,那么他既可以出现在赋值操作符的左边,也可以出现在右边。右值是:只允许出现在赋值操作符右边的表达式。
int a = 42; int b = 43; // a b 都是左值 a = b; // ok b = a; // ok a = a * b; // ok // a * b 是右值 int c = a * b; // ok, 右值出现在=右边 a * b = 42; // error, 右值不允许出现在=左边
那么在c++中,左右值的定义也差不多,但是有一点点变化。在c++中,左值是:你可以对它取地址(&)操作的表达式,右值是非左值的表达式。
// 左值: // int i = 42; i = 43; // ok, i 是左值 int* p = &i; // ok, i 是左值 int& foo(); foo() = 42; // ok, foo() 是左值 int* p1 = &foo(); // ok, foo() 是左值 // 右值: // int foobar(); int j = 0; j = foobar(); // ok, foobar() 是右值 int* p2 = &foobar(); // error, 不能取右值表达式的地址 j = 42; // ok, 42 是右值
假设我们有一个类X,他有一个成员函数为一个指针,可能指向某些资源之类的。那么相应的构造、析构、拷贝等操作都是需要一些特殊的处理工作。
比如我们常见的std::vector,那么X的拷贝操作就类似下面这样:
X& X::operator=(X const & rhs)
{
// [...]
// 先收回资源m_pResource
// 对rhs的m_pResource指向的资源做拷贝操作
// 然后令m_pResource指向新的内存位置.
// [...]
}
或者你可能会遇到下面这种代码:
X foo();
X x;
x = foo();
那么第三句,也就是最后一句要执行三个操作:
1.释放原来x的资源。
2.克隆foo返回值中的资源给x的资源。
3.销毁释放返回值中的资源。
很显然,这是很低效的做法,更聪明的做法是swap操作,交换x和返回值x`的资源,然后原x的资源会被自动释放。
换句话说我们想做的就是:
...
swap(m_pResource,rhs.m_pResource);
...
这个就叫做move语义,在c++11中,通过重载可实现这个特性:
X& X::operator=(<???> rhs)
{
// [...]
// swap(m_pResource,rhs.m_pResource);
// [...]
}
那么这个???是个什么类型呢?当然首先它应该是一种引用类型,减少不必要的值拷贝,然后我们还想它和传统的引用类型分开,因为我们希望当右操作数为右值的时候,调用???这个重载,而当右操作数为左值的时候,调用原始的引用赋值构造,那么好吧,那我们就把???叫做“右值引用”好了。