第 6 章 移动语义与 enable_if()

第 6 章 移动语义与 enable_if()

C++11 引入的最突出的特性之一便是移动语义(move semantics)。 您可以使用它通过将内部资源从源对象移动(“窃取”)到目标对象而不是复制这些内容来优化复制和赋值。如果源不再需要它的内部值或状态(因为它即将失效),就可以做到这一点。

移动语义对模板的设计有重要的影响,在泛型代码中引入了特殊的规则来支持移动语义。本章将介绍这些特性。

6.1 完美转发

假设你想编写泛型代码来转发传递的参数的基本属性:

  • 可变对象被转发后依然可以修改。
  • 常量对象应当被转发为只读对象。
  • 可移动对象(我们可以“窃取”的对象,因为它们即将失效)应当被转发为可移动对象。

要在没有模板的情况下实现此功能,我们必须对所有三种情况进行编程。例如,要将f()的调用转发给相应的函数g()

basics/move1.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <utility>
#include <iostream>

class X { /*...*/ };

void g(X&) { std::cout << "g() for variable\n"; }
void g(X const&) { std::cout << "g() for constant\n"; }
void g(X&&) { std::cout << "g() for movable object\n"; }

// let f() forward argument val to g():
void f(X& val) {
g(val); // val is non-const lvalue => calls g(X&)
}

void f(X const& val) {
g(val); // val is const lvalue => calls g(X const&)
}

void f(X&& val) {
g(std::move(val)); // val is non-const lvalue => needs std::move() to call g(X&&)
}

int main()
{
X v; // create variable
X const c; // create constant
f(v); // f() for nonconstant object calls f(X&) => calls g(X&)
f(c); // f() for constant object calls f(X const&) => calls g(X const&)
f(X()); // f() for temporary calls f(X&&) => calls g(X&&)
f(std::move(v)); // f() for movable variable calls f(X&&) => calls g(X&&)
}

在这里,我们看到f()将其参数转发给g()的三种不同实现:

1
2
3
4
5
6
7
8
9
10
11
void f(X& val) {  
g(val); // val is non-const lvalue => calls g(X&)
}

void f(X const& val) {
g(val); // val is const lvalue => calls g(X const&)
}

void f(X&& val) {
g(std::move(val)); // val is non-const lvalue => needs std::move() to call g(X&&)
}

请注意,可移动对象的代码(通过右值引用)与其他代码不同:它需要一个std::move(),因为根据语言规则,移动语义不被传递。虽然第三个f()中的val被声明为右值引用,但它作为表达式使用时的值类别是一个非常量左值(参见附录 B),并且行为类似于第一个f()中的val。如果没有move(),对于非常量左值,将调用g(X&)而不是g(&&)


第 6 章 移动语义与 enable_if()
http://example.com/2023/08/15/cpp-template-ch/cpp-template-ch06/
作者
QiDianMaker
发布于
2023年8月14日
更新于
2023年8月21日
许可协议