每日小结

#c++11(新?)特性之右值引用

#右值

不能放到赋值表达式左侧的值

例如:

  • 字面量 1, "Hello", 'c'
  • 返回值而非引用的函数调用 foo()
  • 运算结果 a + b

右值不能被直接赋值给一个非常引用, 只能先赋给一个变量再取引用

1
2
3
4
5
int& ref = 9; // error, “invalid initialization of non-const reference of type int& from an rvalue of type int”
const int& const_ref = 9; // only allow this

int nine = 9;
int& ref = nine; // ok

上述特性在泛型函数重载时会带来一些问题, 这导致我们不得不对有无const两种情况分别重载

1
2
3
4
5
6
7
template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
return std::unique_ptr<T>(new T(a1));
}

factory<foo>(5); // error, a1->int&, can not bind to literal 5

#右值引用

顾名思义, 就是对右值的引用

#增加右值引用的好处

增加了右值引用之后, 我们具备了识别和重载右值的能力

#进一步消除不必要的复制

在c++11之前, 临时变量的创建一直是难以处理的问题. 有时这些临时变量可以被编译器优化(例如返回值优化), 但是这并不总是可行的, 通常这会导致高昂的对象复制成本.

考虑下面的代码, 理论上这里最多可能发生两次复制操作:

  1. 返回临时变量

  2. vector赋值

其中, 第一次的复制操作可能会被编译器优化掉(RVO), 然而第二次复制是不可避免的

当然我们可以通过其他方法来避免这次复制, 比如通过指针或者传递一个已经填充好的vector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <vector>
using namespace std;

vector<string> make_poem()
{
vector<string> lines;
lines.push_back("Hello");
lines.push_back("World");
return lines; // 1.
}

int main()
{
vector<string> poem;
poem = make_poem(); // 2.
}

有了右值引用之后, 我们可以就可以对右值进行特殊化处理, 从而避免多余的复制(移动构造函数和=运算符重载), 例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// copy
foo(foo const& other)
{
this->length = other.length;
this->ptr = new int[other.length];
copy(other.ptr, other.ptr + other.length, this->ptr);
}

// 与之对应的move
foo(foo&& other)
{
this->length = other.length;
this->ptr = other.ptr;
other.length = 0;
other.ptr = nullptr;
}

#移动语义的引入

可以显式指定某个资源被移交给另一个函数, 自己不再需要

1
2
Resource resource;
foo(move(resource)); // 不再需要resource, 可以全权交给foo函数, 将会调用foo(Resource&&)

对于有些不能被复制的资源(如std::unique_ptr, std::thread等等), 这是很有用的

1
2
3
std::string s;
std::string another(s); // calls std::string(const std::string&);
std::string more(std::string(s)); // calls std::string(std::string&&);

#模板的完美转发

(TODO)

#实现

move函数: 提醒编译器重载时选择移动构造函数

利用移动交换两个变量:

1
2
3
4
5
6
void move_swap(Res &a, Res &b)
{
Res t = move(a);
a = move(b);
b = move(t);
}

#RVO和NRVO

返回值优化(Return value optimization,缩写为RVO)是C++的一项编译优化技术。即删除保持函数返回值的临时对象。这可能会省略两次复制构造函数,即使复制构造函数有副作用