老外的文章:http://thbecker.net/articles/rvalue_references/section_01.html
C++11 标准才中引入右值引用特性
右值引用主要解决两个问题:
1、实现移动语句 Implementing move semantics
2、更高效的参数传递 Perfect forwarding
这里有两个概念:左值(lvalues) 和 右值(rvalues)
直观的解释就是:
左值:可以在等号左边或右边的表达式
右值:只能出现在等号右边的表达式
int a = 42; int b = 43; // a and b are both l-values: a = b; // ok b = a; // ok a = a * b; // ok // a * b is an rvalue: int c = a * b; // ok, rvalue on right hand side of assignment a * b = 42; // error, rvalue on left hand side of assignment
但是这个定义只能针对普通的数据类型, C++ 中的自定义类型就有些不同了,更进一步的定义:
左值:引用内存块并能用 & 操作符获得内存地址的表达式
右值:不是左值的表达式
// lvalues: int i = 42; // ok, i is an lvalue int* p = &i; // ok, i is an lvalue int& foo(); foo() = 42; // ok, foo() is an lvalue int* p1 = &foo(); // ok, foo() is an lvalue 这里是引用 // rvalues: int foobar(); int j = foobar(); // ok, foobar() is an rvalue int* p2 = &foobar(); // error, cannot take the address of an rvalue 取临时变量的地址会出错
移动语句 Move Semantics
持有资源的类一般的赋值操作:
X& X::operator=(X const & rhs) { // ... // 释放自己的资源 // 使用 rhs 复制构造资源 // ... } //实际调用函数赋值 X foo(); X x; // perhaps use x in various ways x = foo();
使用右值引用实现移动语句赋值:
X& X::operator=(X&& rhs) { // ... // swap this->m_pResource and rhs.m_pResource 直接交换 // ... }
所以在一个类中,需要实现两个赋值操作函数:
X& X::operator=(X const & rhs); // classical implementation X& X::operator=(X&& rhs); //move Semantics
注意函数区别:
void foo(X&); //只能使用左值调用 void foo(X const &); //左值右值都可以调用,但实现方式不一样,需要复制构造 void foo(X&&); //只能用于右值调用
左值引用使用移动语句 Forcing Move Semantics
一个左值对象要使用移动语句,则这个类的 复制构造函数和 赋值操作函数都需要实现移动语句的版本
普通版本的交换函数:
template<class T> void swap(T& a, T& b) { T tmp(a); a = b; b = tmp; } X a, b; swap(a, b);
移动语句版本:
template<class T> void swap(T& a, T& b) { T tmp(std::move(a)); a = std::move(b); b = std::move(tmp); } X a, b; swap(a, b);
函数 std::move 只是简单的将左值引用转换为右值引用
如果交换的对象没有实现移动语句,那这个 swap 就和普通版本的 swap 作用相同,复制构造对象
使用移动语句的类需要注意析构函数释放资源时对其他对象造成影响,如在析构函数中释放锁
因此需要在函数中显式处理析构函数:
X& X::operator=(X&& rhs) { // 先处理析构函数 // Perform a cleanup that takes care of at least those parts of the // destructor that have side effects. Be sure to leave the object // in a destructible and assignable state. // Move semantics: exchange content between this and rhs return *this; }
右值与右值引用 if-it-has-a-name rule
申明为右值引用的变量可以是左值,也可以是右值
void foo(X&& x) { X anotherX = x; // calls X(X const & rhs) 这里作为左值引用使用 }
if-it-has-a-name rule:如果有变量名,则是左值,否则是右值
void foo(X&& x) { X anotherX = x; // calls X(X const & rhs) 左值引用 } X&& goo(); // calls X(X&& rhs) because the thing on the right hand side has no name X x = goo(); //右值引用
所以 std::move(x) 的作用就是“隐藏变量名”,即不管参数是左值还是右值,隐藏变量名然后返回一个右值引用
//基类声明 Base(Base const & rhs); // non-move semantics Base(Base&& rhs); // move semantics //子类实现 Derived(Derived const & rhs) : Base(rhs) { // Derived-specific stuff } //正确的方式 Derived(Derived&& rhs) : Base( std::move(rhs) ) // good, calls Base(Base&& rhs) { // Derived-specific stuff } //错误的方式 Derived(Derived&& rhs) : Base(rhs) // wrong: rhs is an lvalue { // Derived-specific stuff }