这一块主要是讲move语义的,我认为这是在C++0x中,最好的特性之一,因为它几乎可以完全透明的提高效率。
在Stephan T. Lavavej这篇帖子之后,有很多评论,大体上认为C++因为这些特性而变得更复杂了,而难以掌握,另初学者望而生畏。
但是我认为这是值得的,因为C++的宗旨是:“don't pay for what you don't use 不要为你不使用的东西而付出代价”。
由于水平有限,错误之处,请多多指教:
move semantics: the pattern
move语义:模式
Here's a simple class, remote_integer, that stores a pointer to a dynamically allocated int . (This is "remote ownership".) Its default constructor, unary constructor, copy constructor, copy assignment operator, and destructor should all look very familiar to you. I've additionally given it a move constructor and move assignment operator. They're guarded by #ifdef MOVABLE so that I can demonstrate what happens with and without them; real code won't do this.
下面是一个简单的类remote_integer, 这个类存储了指向动态分配的int的指针(“远程拥有权”)。你对这个类的默认构造函数,一元构造函数,拷贝构造函数,重载的赋值运算和析构函数都很熟悉。我给它增加了move构造函数和move 赋值运算。它们被#ifdef MOVABLE 保护,所以我可以演示在没有move构造函数和move 赋值运算时会发生什么,真正的代码是没有这个地。
2
3 #include < stddef.h >
4
5 #include < iostream >
6
7 #include < ostream >
8
9 using namespace std;
10
11
12
13 class remote_integer {
14
15public:
16
17 remote_integer() {
18
19 cout << "Default constructor." << endl;
20
21
22
23 m_p = NULL;
24
25 }
26
27
28
29 explicit remote_integer(const int n) {
30
31 cout << "Unary constructor." << endl;
32
33
34
35 m_p = new int(n);
36
37 }
38
39
40
41 remote_integer(const remote_integer& other) {
42
43 cout << "Copy constructor." << endl;
44
45
46
47 if (other.m_p) {
48
49 m_p = new int(*other.m_p);
50
51 } else {
52
53 m_p = NULL;
54
55 }
56
57 }
58
59
60
61#ifdef MOVABLE
62
63 remote_integer(remote_integer&& other) {
64
65 cout << "MOVE CONSTRUCTOR." << endl;
66
67
68
69 m_p = other.m_p;
70
71 other.m_p = NULL;
72
73 }
74
75#endif // #ifdef MOVABLE
76
77
78
79 remote_integer& operator=(const remote_integer& other) {
80
81 cout << "Copy assignment operator." << endl;
82
83
84
85 if (this != &other) {
86
87 delete m_p;
88
89
90
91 if (other.m_p) {
92
93 m_p = new int(*other.m_p);
94
95 } else {
96
97 m_p = NULL;
98
99 }
100
101 }
102
103
104
105 return *this;
106
107 }
108
109
110
111#ifdef MOVABLE
112
113 remote_integer& operator=(remote_integer&& other) {
114
115 cout << "MOVE ASSIGNMENT OPERATOR." << endl;
116
117
118
119 if (this != &other) {
120
121 delete m_p;
122
123
124
125 m_p = other.m_p;
126
127 other.m_p = NULL;
128
129 }
130
131
132
133 return *this;
134
135 }
136
137#endif // #ifdef MOVABLE
138
139
140
141 ~remote_integer() {
142
143 cout << "Destructor." << endl;
144
145
146
147 delete m_p;
148
149 }
150
151
152
153 int get() const {
154
155 return m_p ? *m_p : 0;
156
157 }
158
159
160
161private:
162
163 int * m_p;
164
165} ;
166
167
168
169 remote_integer square( const remote_integer & r) {
170
171 const int i = r.get();
172
173
174
175 return remote_integer(i * i);
176
177}
178
179
180
181 int main() {
182
183 remote_integer a(8);
184
185
186
187 cout << a.get() << endl;
188
189
190
191 remote_integer b(10);
192
193
194
195 cout << b.get() << endl;
196
197
198
199 b = square(a);
200
201
202
203 cout << b.get() << endl;
204
205}
206
207
208
209 // C:\Temp>cl /EHsc /nologo /W4 remote.cpp
210
211 // remote.cpp
212
213
214
215 // C:\Temp>remote
216
217 Unary constructor.
218
219 8
220
221 Unary constructor.
222
223 10
224
225 Unary constructor.
226
227 Copy assignment operator .
228
229 Destructor.
230
231 64
232
233 Destructor.
234
235 Destructor.
236
237
238
239 // C:\Temp>cl /EHsc /nologo /W4 /DMOVABLE // remote.cpp
240
241 // remote.cpp
242
243
244
245 // C:\Temp>remote
246
247 Unary constructor.
248
249 8
250
251 Unary constructor.
252
253 10
254
255 Unary constructor.
256
257 MOVE ASSIGNMENT OPERATOR.
258
259 Destructor.
260
261 64
262
263 Destructor.
264
265 Destructor.
266
267
There are several things to notice here.
这里有几点需要注意:
· The copy and move constructors are overloaded, and the copy and move assignment operators are overloaded. We've already seen what happens to functions overloaded on const Type& and Type&& . This is what allows b = square(a); to automatically select the move assignment operator when it's available.
拷贝构造函数和move构造函数式重载版本,赋值操作和move赋值操作也是重载版本,我们已经看到当函数通过const Type&和Type&&进行重载时,结果的不同。当move语义可用时,b=square(a)将会选择move赋值操作。
· Instead of dynamically allocating memory, the move constructor and move assignment operator simply steal it from other . When stealing, we copy other's pointer and then null it out. When other is destroyed, its destructor will do nothing.
move构造函数和move赋值操作只是简单的将内存从other那里“偷”了过来,而不是重新分配新的内存。当我们“偷”内存时,我们只是复制other的指向内存的指针,然后other的指针置为null。当other被摧毁时,析构函数什么都不用干(不用delete)。
· Both the copy and move assignment operators need self-assignment checks. It's well-known why copy assignment operators need self-assignment checks. This is because plain old data types like ints can be assigned to themselves harmlessly (e.g. with x = x;), so user-defined data types should also be harmlessly self-assignable. Self-assignment virtually never happens in handwritten code, but it can easily happen inside algorithms like std::sort() . In C++0x, algorithms like std::sort() can move elements around instead of copying them. The same potential for self-assignment exists here.
普通赋值操作和move赋值操作都需要进行自我赋值检查,赋值操作需要进行自我赋值检查是广为人知的。这是因为像int这种内建类型可以正确的自我赋值(例如:x=x),所以,用户的自定义类型也应该可以正确的进行自我赋值,自我赋值实际上在手写代码里面是不存在的,但是在类似std::sort()之类的算法中,却很常见。在C++0x中,像std::sort()之类的算法使用了move语义而非拷贝语义,所以同样的move赋值操作也需要进行自我赋值检查。
At this point, you may be wondering how this interacts with automatically generated ("implicitly declared" in Standardese) constructors and assignment operators.
这样,你可能会想move拷贝构造函数和move赋值操作和编译器自动生成拷贝构造函数和赋值操作之间是什么关系呢,会不会自动生成move的呢。
· Move constructors and move assignment operators are never implicitly declared.
Move构造函数和move赋值操作都不会自动生成。
· The implicit declaration of a default constructor is inhibited by any user-declared constructors, including copy constructors and move constructors.
如果用户声明了构造函数,包括拷贝构造函数和move构造函数,默认构造函数将不会自动生成。
· The implicit declaration of a copy constructor is inhibited by a user-declared copy constructor, but not a user-declared move constructor.
用户定义了拷贝构造函数以后,编译器将不会再自动生成拷贝构造函数,但是move构造函数例外。
· The implicit declaration of a copy assignment operator is inhibited by a user-declared copy assignment operator, but not a user-declared move assignment operator.
用户定义了赋值操作以后,编译器将不会再自动生成赋值操作,但是move赋值操作例外。
Basically, the automatic generation rules don't interact with move semantics, except that declaring a move constructor, like declaring any constructor, inhibits the implicitly declared default constructor.
基本上,除了move构造函数会阻止自动生成默认构造函数以外,自动生成规则不影响move语义。
move semantics: moving from lvalues
move 语义:从左值move
Now, what if you like to write your copy constructors in terms of your copy assignment operators? You might attempt to write your move constructors in terms of your move assignment operators. This is possible, but you have to be careful. Here's the wrong way to do it:
现在,如果你在拷贝构造函数中使用赋值操作会怎么样呢?你可能会在move构造函数中使用你的move赋值操作。这是可能的,但是你必须小心。下面是一种错误的使用方法:
2
3 #include < stddef.h >
4
5 #include < iostream >
6
7 #include < ostream >
8
9 using namespace std;
10
11
12
13 class remote_integer {
14
15public:
16
17 remote_integer() {
18
19 cout << "Default constructor." << endl;
20
21
22
23 m_p = NULL;
24
25 }
26
27
28
29 explicit remote_integer(const int n) {
30
31 cout << "Unary constructor." << endl;
32
33
34
35 m_p = new int(n);
36
37 }
38
39
40
41 remote_integer(const remote_integer& other) {
42
43 cout << "Copy constructor." << endl;
44
45
46
47 m_p = NULL;
48
49 *this = other;
50
51 }
52
53
54
55#ifdef MOVABLE
56
57 remote_integer(remote_integer&& other) {
58
59 cout << "MOVE CONSTRUCTOR." << endl;
60
61
62
63 m_p = NULL;
64
65 *this = other; // WRONG
66
67 }
68
69#endif // #ifdef MOVABLE
70
71
72
73 remote_integer& operator=(const remote_integer& other) {
74
75 cout << "Copy assignment operator." << endl;
76
77
78
79 if (this != &other) {
80
81 delete m_p;
82
83
84
85 if (other.m_p) {
86
87 m_p = new int(*other.m_p);
88
89 } else {
90
91 m_p = NULL;
92
93 }
94
95 }
96
97
98
99 return *this;
100
101 }
102
103
104
105#ifdef MOVABLE
106
107 remote_integer& operator=(remote_integer&& other) {
108
109 cout << "MOVE ASSIGNMENT OPERATOR." << endl;
110
111
112
113 if (this != &other) {
114
115 delete m_p;
116
117
118
119 m_p = other.m_p;
120
121 other.m_p = NULL;
122
123 }
124
125
126
127 return *this;
128
129 }
130
131#endif // #ifdef MOVABLE
132
133
134
135 ~remote_integer() {
136
137 cout << "Destructor." << endl;
138
139
140
141 delete m_p;
142
143 }
144
145
146
147 int get() const {
148
149 return m_p ? *m_p : 0;
150
151 }
152
153
154
155private:
156
157 int * m_p;
158
159} ;
160
161
162
163 remote_integer frumple( const int n) {
164
165 if (n == 1729) {
166
167 return remote_integer(1729);
168
169 }
170
171
172
173 remote_integer ret(n * n);
174
175
176
177 return ret;
178
179}
180
181
182
183 int main() {
184
185 remote_integer x = frumple(5);
186
187
188
189 cout << x.get() << endl;
190
191
192
193 remote_integer y = frumple(1729);
194
195
196
197 cout << y.get() << endl;
198
199}
200
201
202
203 // C:\Temp>cl /EHsc /nologo /W4 /O2 unified_wrong.cpp
204
205 // unified_wrong.cpp
206
207
208
209 // C:\Temp>unified_wrong
210
211 Unary constructor.
212
213 Copy constructor.
214
215 Copy assignment operator .
216
217 Destructor.
218
219 25
220
221 Unary constructor.
222
223 1729
224
225 Destructor.
226
227 Destructor.
228
229
230
231 // C:\Temp>cl /EHsc /nologo /W4 /O2 /DMOVABLE unified_wrong.cpp
232
233 // unified_wrong.cpp
234
235
236
237 // C:\Temp>unified_wrong
238
239 Unary constructor.
240
241 MOVE CONSTRUCTOR.
242
243 Copy assignment operator .
244
245 Destructor.
246
247 25
248
249 Unary constructor.
250
251 1729
252
253 Destructor.
254
255 Destructor.
256
257
(The compiler is performing the RVO here, but not the NRVO. As I mentioned earlier, some copy constructor calls are elided by the RVO and NRVO, but the compiler isn't always able to apply them. Move constructors optimize the remaining cases.)
(编译器进行了返回值优化RVO,但不是具名返回值优化NRVO。之前提到,有些拷贝构造函数被RVO或NRVO优化掉了,但是并不是所有的情况下都能优化掉,move构造函数可以优化剩下的情况。)
The line marked WRONG inside the move constructor is calling the copy assignment operator! This compiles and runs, but it defeats the purpose of the move constructor.
在有WRONG的那一行,在move构造函数中调用了赋值操作,编译通过了,但结果却并不对(因为那个赋值操作调用的是普通的赋值操作)。
What happened? Remember from C++98/03 that named lvalue references are lvalues (if you say int& r = *p; then r is an lvalue) and unnamed lvalue references are also lvalues (given vector<int> v(10, 1729), calling v[0] returns int& , an unnamed lvalue reference whose address you can take). Rvalue references behave differently:
为什么呢?在C++98/03中,具名的左值引用是左值,不具名的左值引用还是左值。但是右值引用却不同:
· Named rvalue references are lvalues.
具名的右值引用是左值。
· Unnamed rvalue references are rvalues.
不具名的右值引用才是右值。
A named rvalue reference is an lvalue because it can be repeatedly mentioned, with multiple operations performed on it. If instead it were an rvalue, then the first operation performed could steal from it, affecting subsequent operations. Stealing is supposed to be unobservable, so this is forbidden. On the other hand, unnamed rvalue references can't be repeatedly mentioned, so they can preserve their rvalueness.
具名的右值引用是左值的原因在于它可以被重复的使用,对它进行各种各样的操作。如果它还是右值的话,在第一个操作中被“偷”走了,后面的操作将会受到影响。“偷”应该不被察觉到(应该和没有“偷”时表现一样),所以不能这样做。另一方面,不具名的右值引用不能被重复的使用,所以它们仍然是右值。
If you're really intent on implementing your move constructors in terms of your move assignment operators, you'll need the ability to move from lvalues by treating them as rvalues. This is powered by std::move() from C++0x <utility>, which will be in VC10 (in fact, it's already in my development build), but because it's not in the VC10 CTP, I'll show you how to write it from scratch:
如果你确实想在move构造函数中使用move赋值操作,你需要把左值当做右值对待。这可以通过使用C++0x <utility>中的std::move()来解决。VC10TCP版本中没有,但是VC10中会有,所以我会教你从头做起:
2
3 #include < stddef.h >
4
5 #include < iostream >
6
7 #include < ostream >
8
9 using namespace std;
10
11
12
13 template < typename T > struct RemoveReference {
14
15 typedef T type;
16
17} ;
18
19
20
21 template < typename T > struct RemoveReference < T &> {
22
23 typedef T type;
24
25} ;
26
27
28
29 template < typename T > struct RemoveReference < T &&> {
30
31 typedef T type;
32
33} ;
34
35
36
37 template < typename T > typename RemoveReference < T > ::type && Move(T && t) {
38
39 return t;
40
41}
42
43
44
45 class remote_integer {
46
47public:
48
49 remote_integer() {
50
51 cout << "Default constructor." << endl;
52
53
54
55 m_p = NULL;
56
57 }
58
59
60
61 explicit remote_integer(const int n) {
62
63 cout << "Unary constructor." << endl;
64
65
66
67 m_p = new int(n);
68
69 }
70
71
72
73 remote_integer(const remote_integer& other) {
74
75 cout << "Copy constructor." << endl;
76
77
78
79 m_p = NULL;
80
81 *this = other;
82
83 }
84
85
86
87#ifdef MOVABLE
88
89 remote_integer(remote_integer&& other) {
90
91 cout << "MOVE CONSTRUCTOR." << endl;
92
93
94
95 m_p = NULL;
96
97 *this = Move(other); // RIGHT
98
99 }
100
101#endif // #ifdef MOVABLE
102
103
104
105 remote_integer& operator=(const remote_integer& other) {
106
107 cout << "Copy assignment operator." << endl;
108
109
110
111 if (this != &other) {
112
113 delete m_p;
114
115
116
117 if (other.m_p) {
118
119 m_p = new int(*other.m_p);
120
121 } else {
122
123 m_p = NULL;
124
125 }
126
127 }
128
129
130
131 return *this;
132
133 }
134
135
136
137#ifdef MOVABLE
138
139 remote_integer& operator=(remote_integer&& other) {
140
141 cout << "MOVE ASSIGNMENT OPERATOR." << endl;
142
143
144
145 if (this != &other) {
146
147 delete m_p;
148
149
150
151 m_p = other.m_p;
152
153 other.m_p = NULL;
154
155 }
156
157
158
159 return *this;
160
161 }
162
163#endif // #ifdef MOVABLE
164
165
166
167 ~remote_integer() {
168
169 cout << "Destructor." << endl;
170
171
172
173 delete m_p;
174
175 }
176
177
178
179 int get() const {
180
181 return m_p ? *m_p : 0;
182
183 }
184
185
186
187private:
188
189 int * m_p;
190
191} ;
192
193
194
195 remote_integer frumple( const int n) {
196
197 if (n == 1729) {
198
199 return remote_integer(1729);
200
201 }
202
203
204
205 remote_integer ret(n * n);
206
207
208
209 return ret;
210
211}
212
213
214
215 int main() {
216
217 remote_integer x = frumple(5);
218
219
220
221 cout << x.get() << endl;
222
223
224
225 remote_integer y = frumple(1729);
226
227
228
229 cout << y.get() << endl;
230
231}
232
233
234
235 // C:\Temp>cl /EHsc /nologo /W4 /O2 /DMOVABLE unified_right.cpp
236
237 // unified_right.cpp
238
239
240
241 // C:\Temp>unified_right
242
243 Unary constructor.
244
245 MOVE CONSTRUCTOR.
246
247 MOVE ASSIGNMENT OPERATOR.
248
249 Destructor.
250
251 25
252
253 Unary constructor.
254
255 1729
256
257 Destructor.
258
259 Destructor.
260
261
(I'll refer to std::move() and my Move() interchangeably, since they're implemented identically.) How does std::move() work? For the moment, I'm going to say that "magic is involved". (There's a full explanation below; it's not complicated, but it involves template argument deduction and reference collapsing, which we'll encounter while looking at perfect forwarding.) I can skip over the magic with concrete examples. Given an lvalue of type string, like up from the overload resolution examples above, std::move(up) calls string&& std::move(string&) . This returns an unnamed rvalue reference, which is an rvalue. Given an rvalue of type string, like strange() from above, std::move(strange()) calls string&& std::move(string&&) . Again, this returns an unnamed rvalue reference, which is an rvalue.
(我将交替的使用std::move和Move,因为它们的实现时一样的)它是怎样工作的呢?现在,我要说“这是个魔法”(后面将会有完整的解释,并不是很复杂,但是它与模板参数演绎和引用合并有关,在讲完美转发的时候我们还会遇到)。我可以用一个具体的例子来略过这个“魔法”:给定一个string类型的左值,像前面例子中的up,std::move(up)调用 string&& std::move(string&), 这个函数返回一个不具名的右值引用,也就是右值。给定一个string类型的右值,像前面例子中的strange(), std::(strange()) 调用 string&& std::move(string&&),这个函数还是返回一个不具名的右值,还是右值。
std::move() is useful in other places than implementing move constructors in terms of move assignment operators. Whenever you reach a point where you can say "I have an lvalue here, and its value is going to cease to matter" (e.g. because it's going to be destroyed or assigned to), you can write std::move(your lvalue expression) in order to activate move semantics.
std::move除了能实现move构造函数中是呀move赋值操作以外,还可以在其他地方使用。无论在任何时候,你说“我有一个左值,但是它的值已经不再重要了”,这时你可以使用std::move(你的左值)来使用move语义。
move semantics: movable members
move语义:movable成员
C++0x's Standard classes (e.g. vector, string, regex) have move constructors and move assignment operators, and we've seen how to implement them in our own classes that manually manage resources (e.g. remote_integer). But what about classes containing movable data members (e.g. vector, string, regex, remote_integer)? The compiler won't automatically generate move constructors and move assignment operators for us. So, we'll need to write them ourselves. Fortunately, with std::move() , this is extremely easy:
C++0x标准类(像 vector, string, regex) 都实现了move构造函数和move赋值操作,而且我们已经学习了怎样在我们自己的类中实现它们来手动管理资源(像 remote_integer). 但是当类中含有movable成员变量是,会怎样呢 (像 vector, string, regex, remote_integer)? 编译器不会自动生成move构造函数和move赋值操作。所以,我们要自己实现,幸运的是我们有了std::move后,实现起来非常简单:
2
3 #include < stddef.h >
4
5 #include < iostream >
6
7 #include < ostream >
8
9 using namespace std;
10
11
12
13 template < typename T > struct RemoveReference {
14
15 typedef T type;
16
17} ;
18
19
20
21 template < typename T > struct RemoveReference < T &> {
22
23 typedef T type;
24
25} ;
26
27
28
29 template < typename T > struct RemoveReference < T &&> {
30
31 typedef T type;
32
33} ;
34
35
36
37 template < typename T > typename RemoveReference < T > ::type && Move(T && t) {
38
39 return t;
40
41}
42
43
44
45 class remote_integer {
46
47public:
48
49 remote_integer() {
50
51 cout << "Default constructor." << endl;
52
53
54
55 m_p = NULL;
56
57 }
58
59
60
61 explicit remote_integer(const int n) {
62
63 cout << "Unary constructor." << endl;
64
65
66
67 m_p = new int(n);
68
69 }
70
71
72
73 remote_integer(const remote_integer& other) {
74
75 cout << "Copy constructor." << endl;
76
77
78
79 if (other.m_p) {
80
81 m_p = new int(*other.m_p);
82
83 } else {
84
85 m_p = NULL;
86
87 }
88
89 }
90
91
92
93 remote_integer(remote_integer&& other) {
94
95 cout << "MOVE CONSTRUCTOR." << endl;
96
97
98
99 m_p = other.m_p;
100
101 other.m_p = NULL;
102
103 }
104
105
106
107 remote_integer& operator=(const remote_integer& other) {
108
109 cout << "Copy assignment operator." << endl;
110
111
112
113 if (this != &other) {
114
115 delete m_p;
116
117
118
119 if (other.m_p) {
120
121 m_p = new int(*other.m_p);
122
123 } else {
124
125 m_p = NULL;
126
127 }
128
129 }
130
131
132
133 return *this;
134
135 }
136
137
138
139 remote_integer& operator=(remote_integer&& other) {
140
141 cout << "MOVE ASSIGNMENT OPERATOR." << endl;
142
143
144
145 if (this != &other) {
146
147 delete m_p;
148
149
150
151 m_p = other.m_p;
152
153 other.m_p = NULL;
154
155 }
156
157
158
159 return *this;
160
161 }
162
163
164
165 ~remote_integer() {
166
167 cout << "Destructor." << endl;
168
169
170
171 delete m_p;
172
173 }
174
175
176
177 int get() const {
178
179 return m_p ? *m_p : 0;
180
181 }
182
183
184
185private:
186
187 int * m_p;
188
189} ;
190
191
192
193 class remote_point {
194
195public:
196
197 remote_point(const int x_arg, const int y_arg)
198
199 : m_x(x_arg), m_y(y_arg) { }
200
201
202
203 remote_point(remote_point&& other)
204
205 : m_x(Move(other.m_x)),
206
207 m_y(Move(other.m_y)) { }
208
209
210
211 remote_point& operator=(remote_point&& other) {
212
213 m_x = Move(other.m_x);
214
215 m_y = Move(other.m_y);
216
217 return *this;
218
219 }
220
221
222
223 int x() const { return m_x.get(); }
224
225 int y() const { return m_y.get(); }
226
227
228
229private:
230
231 remote_integer m_x;
232
233 remote_integer m_y;
234
235} ;
236
237
238
239 remote_point five_by_five() {
240
241 return remote_point(5, 5);
242
243}
244
245
246
247 remote_point taxicab( const int n) {
248
249 if (n == 0) {
250
251 return remote_point(1, 1728);
252
253 }
254
255
256
257 remote_point ret(729, 1000);
258
259
260
261 return ret;
262
263}
264
265
266
267 int main() {
268
269 remote_point p = taxicab(43112609);
270
271
272
273 cout << "(" << p.x() << ", " << p.y() << ")" << endl;
274
275
276
277 p = five_by_five();
278
279
280
281 cout << "(" << p.x() << ", " << p.y() << ")" << endl;
282
283}
284
285
286
287 // C:\Temp>cl /EHsc /nologo /W4 /O2 point.cpp
288
289 // point.cpp
290
291
292
293 // C:\Temp>point
294
295 Unary constructor.
296
297 Unary constructor.
298
299 MOVE CONSTRUCTOR.
300
301 MOVE CONSTRUCTOR.
302
303 Destructor.
304
305 Destructor.
306
307 ( 729 , 1000 )
308
309 Unary constructor.
310
311 Unary constructor.
312
313 MOVE ASSIGNMENT OPERATOR.
314
315 MOVE ASSIGNMENT OPERATOR.
316
317 Destructor.
318
319 Destructor.
320
321 ( 5 , 5 )
322
323 Destructor.
324
325 Destructor.
326
327
As you can see, memberwise moves are trivial to write. Note that remote_point's move assignment operator doesn't need to check for self-assignment because remote_integer already does. Also note that remote_point's implicitly declared copy constructor, copy assignment operator, and destructor do the right things.
和你看到的一样,move成员是很无聊的。注意remote_point的move赋值操作没有进行自我赋值检查,因为remote_integer已经检查过了。同样也注意到remote_point自动生成的拷贝构造函数,赋值操作和析构函数都工作正常。
A final reminder: whenever possible, you should implement move constructors and move assignment operators for your copyable classes, because the compiler won't do it for you. Not only will ordinary use of those classes automatically pick up move semantics, but STL containers and algorithms will also take advantage of move semantics, replacing expensive copies with cheap moves.
最后提示,无论在任何时候,你都应该为copyable的类实现move构造函数和move赋值函数,因为编译器不会自动生成。并不只是因为move语义会在通常情况下被使用,而是STL容器和算法会从move语义中得到好处:用简单的move代替昂贵的拷贝。