右值引用之移动语义

本文翻译自关于右值引用解释的经典文章,如果英文还可以的话,直接去看英文原文。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);
  // [...]  
}

 那么这个???是个什么类型呢?当然首先它应该是一种引用类型,减少不必要的值拷贝,然后我们还想它和传统的引用类型分开,因为我们希望当右操作数为右值的时候,调用???这个重载,而当右操作数为左值的时候,调用原始的引用赋值构造,那么好吧,那我们就把???叫做“右值引用”好了。

原文地址:https://www.cnblogs.com/houhoujun/p/4403802.html