感觉又来谈谈右值引用了,这次带上了移动构造函数一起谈。初学移动构造函数时,我看到的大致能总结成这么一句话:资源的转移而非拷贝。由于我初学C++(之前写算法题不能算学了C++),很多隐藏的东西我很难搞懂,很多描述令我抓耳挠腮:有的描述说,右值引用可以延长对象的生命周期。怎么个延长法?本来在栈上的东西出了作用域就无了,延长的是谁的生命周期?对于移动构造函数来说,延长的是整个临时对象的生命周期吗?

答案在C++ Primer里说的其实已经挺清楚了:

与拷贝构造函数不同,移动构造函数不分配任何新内存;它接管给定的StrVec中的内存。在接管内存之后,它将给定对象中的指针都置为nullptr。这样就完成了从给定对象的移动操作,此对象将继续存在。最终,移后源对象会被销毁,意味着将在其上运行析构函数。StrVec的析构函数在first_free上调用deallocate。如果我们忘记了改变s.first_free,则销毁移后源对象就会释放掉我们刚刚移动的内存。

此处要注意两点:一是右值引用让编译器知道,这个引用(就是一个存地址的指针)绑定的是一个临时对象,该对象在之后不会被用到,那么编译器就可以安全地转移资源。

什么是资源?一个类A有如下成员变量:

1
2
3
4
5
6
class A
{
private:
int i;
std::string s;
}

由于我实在头脑不清晰。我在最开始理解移动构造的时候,认为这种简单类型i也要进行移动。如果这个对象创建到了栈上,比如这样:

1
2
3
4
5
int main()
{
A a1(1,"a1");
...
}

那么i这个变量也应该在栈上。那既然如此,我愚蠢地认为,出现了一个右值引用,引用到了栈上的这个i的变量。那函数结束后,作用域结束,该函数的栈都空了,哪去找这个i的变量引用?

1
2
3
4
A(A&& other) noexcept : a(other.a), s(std::move(other.s)) {
std::cout << "Move Construct: " << s << ", a=" << a << "\n";
other.a = 0;
}

但是对于string来说比较好理解。string这个字符串实际是建立在堆上的,这里实际上拿的是一个指针。那么右值引用就告诉编译器,这是块后面不会再用到的资源,可以进行转移。于是把这个指针指向的地址(这个地址在堆)复制到新的对象里就好了。不需要在堆里再做资源的拷贝。但一定要注意。在退出移动构造函数之前,如果是自己定义的持有资源的对象,一定要将其清零。不然原对象会销毁我们已经移交了控制权的那块资源。此处string没有做是因为:std::move(other.s)来初始化新对象的s,这会调用std::string的移动构造函数),所以other.s已经被std::string的移动构造函数置为空字符串

那么这种简单变量怎么办呢?

这时候愚蠢的我发现,移动构造函数可以我们自己写,一般直接赋值即可,也就是简单的拷贝操作:

1
2
3
4
5
6
7
8
9
10
// 移动赋值
A& operator=(A&& other) noexcept {
if (this != &other) {
a = other.a;
s = std::move(other.s);
other.a = 0;
std::cout << "Move Assign: " << s << ", a=" << a << "\n";
}
return *this;
}

这就解决了我的疑惑。我总是认为移动,就是都得移动!一点拷贝都不要沾!实际上是个非常片面、错误的想法。所以,移动构造函数主要利于持有资源的对象(简单理解为指针就行,就是避免把指针指向的东西再拷贝一遍,减少内存开销)。如果对象就只有一堆简单变量而已,那么写的移动构造函数和拷贝构造就没什么区别了。

再回到左右值,右值引用。

我真的很害怕这种文字游戏,用高级概念来解释高级概念,只会让头脑本就不清晰的我变得更笨。

把这些名词放到一遍。本质上,就是编译器对这些地址存放的东西打标记。编译器关心什么呢?它要知道的是:表达式有值类别(value category)以及类型系统附带生命周期信息。

我一开始会把“右值引用”和“右值本身”混为一谈,觉得 T&& 就意味着“这是一个右值”。其实更准确的理解是:右值引用并不是右值本身,而是一种给右值起别名的能力。右值对象通常是临时值,它本身可以直接绑定到右值引用上,例如:

1
A&& r = A(); 

在编译器眼里,这段代码等价于:先创建了一个临时对象 A(),然后给它取了一个名字 r,于是这个临时对象就有了一个别名。但需要注意的是,这个对象依然被编译器标记为“可被消耗的对象”,也就是允许对它进行资源转移。所以接下来完全可以写:

1
A b = std::move(r);

这意味着右值引用可以捕获一个“将要销毁的对象”,并把这种“可以被破坏性使用”的标记继续传递下去。std::move 的本质其实非常简单,它甚至可以写成这样:

1
2
3
4
template<class T>
T&& move(T& t) {
return static_cast<T&&>(t);
}

它并不执行任何移动操作,只是把一个左值转换成右值引用类型,从而告诉编译器允许对它进行移动语义上的优化。真正的移动行为仍然发生在移动构造函数或移动赋值运算符中。

接下来有一个很容易误解的地方:右值引用变量本身是左值。

1
2
3
A&& x = A();
A y = x; // 这里会调用拷贝
A z = std::move(x); // 这里才会调用移动

原因很简单:只要一个对象有名字,它就被编译器视为可寻址对象,因此具有左值语义;但它的类型却是 T&&,这意味着我们可以显式地把它再转换回右值类别。因此可以总结为:右值引用变量,其实是一个“绑定到右值上的左值”。它只是提供了一条绑定右值的渠道,而不是永久地把该对象钉死为右值。从优化角度看,右值引用最大的价值在于给编译器一个静态承诺:这个对象的生命周期即将结束,因此可以放心地转移资源,避免额外拷贝。

用一句话来说,右值引用代表的是:生命周期和使用方式上的承诺。编译器据此可以跨函数传递“允许破坏源对象”的权限,从而减少堆分配、避免深拷贝、优化容器重分配,并允许在返回大对象时仍然保持零开销。在拷贝构造函数中,编译器必须保证语义不变,因此不能破坏源对象(随便拿到别的对象拥有的资源并改变就是破坏了),只能重新开辟空间并复制内容;而在移动构造函数中,编译器已经得到了“可以破坏源对象”的授权,于是资源可以直接转移,爱怎么破坏就怎么破坏(),源对象只需要被置为空即可。那这么理解右值引用比较好,它把允许破坏源对象这件事情合法化并显式化,同时也给了编译器充分的优化空间。

这个网站好用啊:https://godbolt.org/

写了个测试代码,可以看看汇编在干什么:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <iostream>
#include <string>

class A {
public:
int a;
std::string s;

// 普通构造
A(int la, std::string ls) : a(la), s(std::move(ls)) {
std::cout << "Construct: " << s << ", a=" << a << "\n";
}

// 拷贝构造
A(const A& other) : a(other.a), s(other.s) {
std::cout << "Copy Construct: " << s << ", a=" << a << "\n";
}

// 移动构造
A(A&& other) noexcept : a(other.a), s(std::move(other.s)) {
std::cout << "Move Construct: " << s << ", a=" << a << "\n";
other.a = 0;
}

// 拷贝赋值
A& operator=(const A& other) {
if (this != &other) {
a = other.a;
s = other.s;
std::cout << "Copy Assign: " << s << ", a=" << a << "\n";
}
return *this;
}

// 移动赋值
A& operator=(A&& other) noexcept {
if (this != &other) {
a = other.a;
s = std::move(other.s);
other.a = 0;
std::cout << "Move Assign: " << s << ", a=" << a << "\n";
}
return *this;
}

~A() {
std::cout << "Destruct: " << s << ", a=" << a << "\n";
}
};

// 返回值测试
A return_by_value() {
A temp(100, "temp");
return temp; // NRVO/移动构造
}

// 返回引用测试
A& return_by_ref(A& obj) {
return obj;
}

// 返回右值引用测试
A&& return_by_rvalue_ref(A& obj) {
return std::move(obj);
}

int main() {
std::cout << "=== 创建 a1 ===\n";
A a1(1, "a1");

std::cout << "\n=== 拷贝构造 a2(a1) ===\n";
A a2(a1);

std::cout << "\n=== 移动构造 a3(std::move(a1)) ===\n";
A a3(std::move(a1));

std::cout << "\n=== 拷贝赋值 a2 = a3 ===\n";
a2 = a3;

std::cout << "\n=== 移动赋值 a3 = std::move(a2) ===\n";
a3 = std::move(a2);

std::cout << "\n=== 返回值测试 ===\n";
A a4 = return_by_value();

std::cout << "\n=== 返回引用测试 ===\n";
A& ref_a = return_by_ref(a3);
std::cout << "ref_a.s = " << ref_a.s << "\n";

std::cout << "\n=== 返回右值引用测试 ===\n";
A a5 = return_by_rvalue_ref(a3);

std::cout << "\n=== End of main ===\n";
}

执行结果:

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
32
33
34
E:\UnrealWorld\project\Learncpp\x64\Debug\Learncpp.exe
=== 创建 a1 ===
Construct: a1, a=1

=== 拷贝构造 a2(a1) ===
Copy Construct: a1, a=1

=== 移动构造 a3(std::move(a1)) ===
Move Construct: a1, a=1

=== 拷贝赋值 a2 = a3 ===
Copy Assign: a1, a=1

=== 移动赋值 a3 = std::move(a2) ===
Move Assign: a1, a=1

=== 返回值测试 ===
Construct: temp, a=100

=== 返回引用测试 ===
ref_a.s = a1

=== 返回右值引用测试 ===
Move Construct: a1, a=1

=== End of main ===
Destruct: a1, a=1
Destruct: temp, a=100
Destruct: , a=0
Destruct: , a=0
Destruct: , a=0

Process finished with exit code 0.

汇编长这样:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
# License: MSVC Proprietary
# The use of this compiler is only permitted for internal evaluation purposes and is otherwise governed by the MSVC License Agreement.
# See https://visualstudio.microsoft.com/license-terms/vs2022-ga-community/
$SG35785 DB 'temp', 00H
ORG $+3
$SG35831 DB '=== ', 0e5H, 088H, 09bH, 0e5H, 0bbH, 0baH, ' a1 ===', 0aH
DB 00H
ORG $+1
$SG35832 DB 'a1', 00H
ORG $+1
$SG35833 DB 0aH, '=== ', 0e6H, 08bH, 0b7H, 0e8H, 0b4H, 09dH, 0e6H, 09eH
DB 084H, 0e9H, 080H, 0a0H, ' a2(a1) ===', 0aH, 00H
ORG $+2
$SG35834 DB 0aH, '=== ', 0e7H, 0a7H, 0bbH, 0e5H, 08aH, 0a8H, 0e6H, 09eH
DB 084H, 0e9H, 080H, 0a0H, ' a3(std::move(a1)) ===', 0aH, 00H
ORG $+7
$SG35835 DB 0aH, '=== ', 0e6H, 08bH, 0b7H, 0e8H, 0b4H, 09dH, 0e8H, 0b5H
DB 08bH, 0e5H, 080H, 0bcH, ' a2 = a3 ===', 0aH, 00H
ORG $+1
$SG35836 DB 0aH, '=== ', 0e7H, 0a7H, 0bbH, 0e5H, 08aH, 0a8H, 0e8H, 0b5H
DB 08bH, 0e5H, 080H, 0bcH, ' a3 = std::move(a2) ===', 0aH, 00H
ORG $+6
$SG35837 DB 0aH, '=== ', 0e8H, 0bfH, 094H, 0e5H, 09bH, 09eH, 0e5H, 080H
DB 0bcH, 0e6H, 0b5H, 08bH, 0e8H, 0afH, 095H, ' ===', 0aH, 00H
ORG $+6
$SG35838 DB 0aH, '=== ', 0e8H, 0bfH, 094H, 0e5H, 09bH, 09eH, 0e5H, 0bcH
DB 095H, 0e7H, 094H, 0a8H, 0e6H, 0b5H, 08bH, 0e8H, 0afH, 095H, ' '
DB '===', 0aH, 00H
ORG $+3
$SG35839 DB 0aH, 00H
ORG $+6
$SG35840 DB 'ref_a.s = ', 00H
ORG $+5
$SG35841 DB 0aH, '=== ', 0e8H, 0bfH, 094H, 0e5H, 09bH, 09eH, 0e5H, 08fH
DB 0b3H, 0e5H, 080H, 0bcH, 0e5H, 0bcH, 095H, 0e7H, 094H, 0a8H, 0e6H
DB 0b5H, 08bH, 0e8H, 0afH, 095H, ' ===', 0aH, 00H
ORG $+5
$SG35842 DB 0aH, '=== End of main ===', 0aH, 00H
`string' DB 'Destruct: ', 00H ; `string'
`string' DB 'Move Assign: ', 00H ; `string'
`string' DB 'Copy Assign: ', 00H ; `string'
`string' DB 'Move Construct: ', 00H ; `string'
`string' DB 'Copy Construct: ', 00H ; `string'
`string' DB 'Construct: ', 00H ; `string'
`string' DB ', a=', 00H ; `string'
`string' DB 0aH, 00H ; `string'

tv79 = 32
this$ = 64
la$ = 72
ls$ = 80
A::A(int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >) PROC ; A::A, COMDAT
$LN5:
mov QWORD PTR [rsp+24], r8
mov DWORD PTR [rsp+16], edx
mov QWORD PTR [rsp+8], rcx
sub rsp, 56 ; 00000038H
mov rax, QWORD PTR this$[rsp]
mov ecx, DWORD PTR la$[rsp]
mov DWORD PTR [rax], ecx
mov rcx, QWORD PTR ls$[rsp]
call std::basic_string<char,std::char_traits<char>,std::allocator<char> > && std::move<std::basic_string<char,std::char_traits<char>,std::allocator<char> > &>(std::basic_string<char,std::char_traits<char>,std::allocator<char> > &) ; std::move<std::basic_string<char,std::char_traits<char>,std::allocator<char> > &>
mov rcx, QWORD PTR this$[rsp]
add rcx, 8
mov rdx, rax
call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >(std::basic_string<char,std::char_traits<char>,std::allocator<char> > &&) ; std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >
npad 1
mov rax, QWORD PTR this$[rsp]
add rax, 8
mov QWORD PTR tv79[rsp], rax
lea rdx, OFFSET FLAT:`string'
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR tv79[rsp]
mov rdx, rcx
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<char,std::char_traits<char>,std::allocator<char> >(std::basic_ostream<char,std::char_traits<char> > &,std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &) ; std::operator<<<char,std::char_traits<char>,std::allocator<char> >
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR this$[rsp]
mov edx, DWORD PTR [rcx]
mov rcx, rax
call QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > & std::basic_ostream<char,std::char_traits<char> >::operator<<(int)
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
npad 1
mov rcx, QWORD PTR ls$[rsp]
call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> >(void) ; std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> >
mov rax, QWORD PTR this$[rsp]
add rsp, 56 ; 00000038H
ret 0
A::A(int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >) ENDP ; A::A

tv78 = 32
this$ = 64
other$ = 72
A::A(A const &) PROC ; A::A, COMDAT
$LN4:
mov QWORD PTR [rsp+16], rdx
mov QWORD PTR [rsp+8], rcx
sub rsp, 56 ; 00000038H
mov rax, QWORD PTR this$[rsp]
mov rcx, QWORD PTR other$[rsp]
mov ecx, DWORD PTR [rcx]
mov DWORD PTR [rax], ecx
mov rax, QWORD PTR other$[rsp]
add rax, 8
mov rcx, QWORD PTR this$[rsp]
add rcx, 8
mov rdx, rax
call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >(std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &) ; std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >
npad 1
mov rax, QWORD PTR this$[rsp]
add rax, 8
mov QWORD PTR tv78[rsp], rax
lea rdx, OFFSET FLAT:`string'
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR tv78[rsp]
mov rdx, rcx
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<char,std::char_traits<char>,std::allocator<char> >(std::basic_ostream<char,std::char_traits<char> > &,std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &) ; std::operator<<<char,std::char_traits<char>,std::allocator<char> >
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR this$[rsp]
mov edx, DWORD PTR [rcx]
mov rcx, rax
call QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > & std::basic_ostream<char,std::char_traits<char> >::operator<<(int)
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
npad 1
mov rax, QWORD PTR this$[rsp]
add rsp, 56 ; 00000038H
ret 0
A::A(A const &) ENDP ; A::A

tv80 = 32
this$ = 64
other$ = 72
A::A(A &&) PROC ; A::A, COMDAT
$LN4:
mov QWORD PTR [rsp+16], rdx
mov QWORD PTR [rsp+8], rcx
sub rsp, 56 ; 00000038H
mov rax, QWORD PTR this$[rsp]
mov rcx, QWORD PTR other$[rsp]
mov ecx, DWORD PTR [rcx]
mov DWORD PTR [rax], ecx
mov rax, QWORD PTR other$[rsp]
add rax, 8
mov rcx, rax
call std::basic_string<char,std::char_traits<char>,std::allocator<char> > && std::move<std::basic_string<char,std::char_traits<char>,std::allocator<char> > &>(std::basic_string<char,std::char_traits<char>,std::allocator<char> > &) ; std::move<std::basic_string<char,std::char_traits<char>,std::allocator<char> > &>
mov rcx, QWORD PTR this$[rsp]
add rcx, 8
mov rdx, rax
call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >(std::basic_string<char,std::char_traits<char>,std::allocator<char> > &&) ; std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >
mov rax, QWORD PTR this$[rsp]
add rax, 8
mov QWORD PTR tv80[rsp], rax
lea rdx, OFFSET FLAT:`string'
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR tv80[rsp]
mov rdx, rcx
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<char,std::char_traits<char>,std::allocator<char> >(std::basic_ostream<char,std::char_traits<char> > &,std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &) ; std::operator<<<char,std::char_traits<char>,std::allocator<char> >
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR this$[rsp]
mov edx, DWORD PTR [rcx]
mov rcx, rax
call QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > & std::basic_ostream<char,std::char_traits<char> >::operator<<(int)
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rax, QWORD PTR other$[rsp]
mov DWORD PTR [rax], 0
mov rax, QWORD PTR this$[rsp]
add rsp, 56 ; 00000038H
ret 0
A::A(A &&) ENDP ; A::A

tv76 = 32
this$ = 64
other$ = 72
A & A::operator=(A const &) PROC ; A::operator=, COMDAT
$LN4:
mov QWORD PTR [rsp+16], rdx
mov QWORD PTR [rsp+8], rcx
sub rsp, 56 ; 00000038H
mov rax, QWORD PTR other$[rsp]
cmp QWORD PTR this$[rsp], rax
je $LN2@operator
mov rax, QWORD PTR this$[rsp]
mov rcx, QWORD PTR other$[rsp]
mov ecx, DWORD PTR [rcx]
mov DWORD PTR [rax], ecx
mov rax, QWORD PTR other$[rsp]
add rax, 8
mov rcx, QWORD PTR this$[rsp]
add rcx, 8
mov rdx, rax
call std::basic_string<char,std::char_traits<char>,std::allocator<char> > & std::basic_string<char,std::char_traits<char>,std::allocator<char> >::operator=(std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &) ; std::basic_string<char,std::char_traits<char>,std::allocator<char> >::operator=
mov rax, QWORD PTR this$[rsp]
add rax, 8
mov QWORD PTR tv76[rsp], rax
lea rdx, OFFSET FLAT:`string'
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR tv76[rsp]
mov rdx, rcx
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<char,std::char_traits<char>,std::allocator<char> >(std::basic_ostream<char,std::char_traits<char> > &,std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &) ; std::operator<<<char,std::char_traits<char>,std::allocator<char> >
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR this$[rsp]
mov edx, DWORD PTR [rcx]
mov rcx, rax
call QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > & std::basic_ostream<char,std::char_traits<char> >::operator<<(int)
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
npad 1
$LN2@operator:
mov rax, QWORD PTR this$[rsp]
add rsp, 56 ; 00000038H
ret 0
A & A::operator=(A const &) ENDP ; A::operator=

tv79 = 32
this$ = 64
other$ = 72
A & A::operator=(A &&) PROC ; A::operator=, COMDAT
$LN5:
mov QWORD PTR [rsp+16], rdx
mov QWORD PTR [rsp+8], rcx
sub rsp, 56 ; 00000038H
mov rax, QWORD PTR other$[rsp]
cmp QWORD PTR this$[rsp], rax
je $LN2@operator
mov rax, QWORD PTR this$[rsp]
mov rcx, QWORD PTR other$[rsp]
mov ecx, DWORD PTR [rcx]
mov DWORD PTR [rax], ecx
mov rax, QWORD PTR other$[rsp]
add rax, 8
mov rcx, rax
call std::basic_string<char,std::char_traits<char>,std::allocator<char> > && std::move<std::basic_string<char,std::char_traits<char>,std::allocator<char> > &>(std::basic_string<char,std::char_traits<char>,std::allocator<char> > &) ; std::move<std::basic_string<char,std::char_traits<char>,std::allocator<char> > &>
mov rcx, QWORD PTR this$[rsp]
add rcx, 8
mov rdx, rax
call std::basic_string<char,std::char_traits<char>,std::allocator<char> > & std::basic_string<char,std::char_traits<char>,std::allocator<char> >::operator=(std::basic_string<char,std::char_traits<char>,std::allocator<char> > &&) ; std::basic_string<char,std::char_traits<char>,std::allocator<char> >::operator=
mov rax, QWORD PTR other$[rsp]
mov DWORD PTR [rax], 0
mov rax, QWORD PTR this$[rsp]
add rax, 8
mov QWORD PTR tv79[rsp], rax
lea rdx, OFFSET FLAT:`string'
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR tv79[rsp]
mov rdx, rcx
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<char,std::char_traits<char>,std::allocator<char> >(std::basic_ostream<char,std::char_traits<char> > &,std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &) ; std::operator<<<char,std::char_traits<char>,std::allocator<char> >
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR this$[rsp]
mov edx, DWORD PTR [rcx]
mov rcx, rax
call QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > & std::basic_ostream<char,std::char_traits<char> >::operator<<(int)
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
npad 1
$LN2@operator:
mov rax, QWORD PTR this$[rsp]
add rsp, 56 ; 00000038H
ret 0
A & A::operator=(A &&) ENDP ; A::operator=

tv71 = 32
this$ = 64
A::~A(void) PROC ; A::~A, COMDAT
$LN4:
mov QWORD PTR [rsp+8], rcx
sub rsp, 56 ; 00000038H
mov rax, QWORD PTR this$[rsp]
add rax, 8
mov QWORD PTR tv71[rsp], rax
lea rdx, OFFSET FLAT:`string'
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR tv71[rsp]
mov rdx, rcx
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<char,std::char_traits<char>,std::allocator<char> >(std::basic_ostream<char,std::char_traits<char> > &,std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &) ; std::operator<<<char,std::char_traits<char>,std::allocator<char> >
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR this$[rsp]
mov edx, DWORD PTR [rcx]
mov rcx, rax
call QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > & std::basic_ostream<char,std::char_traits<char> >::operator<<(int)
lea rdx, OFFSET FLAT:`string'
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
npad 1
mov rax, QWORD PTR this$[rsp]
add rax, 8
mov rcx, rax
call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> >(void) ; std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> >
npad 1
add rsp, 56 ; 00000038H
ret 0
A::~A(void) ENDP ; A::~A

$T1 = 32
$T2 = 40
tv83 = 48
$T3 = 56
temp$ = 88
__$ArrayPad$ = 128
__$ReturnUdt$ = 160
A return_by_value(void) PROC ; return_by_value
$LN7:
mov QWORD PTR [rsp+8], rcx
sub rsp, 152 ; 00000098H
mov rax, QWORD PTR __security_cookie
xor rax, rsp
mov QWORD PTR __$ArrayPad$[rsp], rax
mov DWORD PTR $T1[rsp], 0
lea rax, QWORD PTR $T3[rsp]
mov QWORD PTR $T2[rsp], rax
lea rdx, OFFSET FLAT:$SG35785
mov rcx, QWORD PTR $T2[rsp]
call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >(char const * const) ; std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >
mov QWORD PTR tv83[rsp], rax
mov r8, QWORD PTR tv83[rsp]
mov edx, 100 ; 00000064H
lea rcx, QWORD PTR temp$[rsp]
call A::A(int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >) ; A::A
npad 1
lea rdx, QWORD PTR temp$[rsp]
mov rcx, QWORD PTR __$ReturnUdt$[rsp]
call A::A(A &&) ; A::A
mov eax, DWORD PTR $T1[rsp]
or eax, 1
mov DWORD PTR $T1[rsp], eax
lea rcx, QWORD PTR temp$[rsp]
call A::~A(void) ; A::~A
mov rax, QWORD PTR __$ReturnUdt$[rsp]
mov rcx, QWORD PTR __$ArrayPad$[rsp]
xor rcx, rsp
call __security_check_cookie
add rsp, 152 ; 00000098H
ret 0
A return_by_value(void) ENDP ; return_by_value

obj$ = 8
A & return_by_ref(A &) PROC ; return_by_ref
mov QWORD PTR [rsp+8], rcx
mov rax, QWORD PTR obj$[rsp]
ret 0
A & return_by_ref(A &) ENDP ; return_by_ref

obj$ = 48
A && return_by_rvalue_ref(A &) PROC ; return_by_rvalue_ref
$LN3:
mov QWORD PTR [rsp+8], rcx
sub rsp, 40 ; 00000028H
mov rcx, QWORD PTR obj$[rsp]
call A && std::move<A &>(A &) ; std::move<A &>
add rsp, 40 ; 00000028H
ret 0
A && return_by_rvalue_ref(A &) ENDP ; return_by_rvalue_ref

$T1 = 32
tv165 = 40
ref_a$ = 48
tv148 = 56
$T2 = 64
a3$ = 96
a2$ = 136
a1$ = 176
a5$ = 216
a4$ = 256
__$ArrayPad$ = 296
main PROC
$LN9:
sub rsp, 312 ; 00000138H
mov rax, QWORD PTR __security_cookie
xor rax, rsp
mov QWORD PTR __$ArrayPad$[rsp], rax
lea rdx, OFFSET FLAT:$SG35831
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
npad 1
lea rax, QWORD PTR $T2[rsp]
mov QWORD PTR $T1[rsp], rax
lea rdx, OFFSET FLAT:$SG35832
mov rcx, QWORD PTR $T1[rsp]
call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >(char const * const) ; std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >
mov QWORD PTR tv165[rsp], rax
mov r8, QWORD PTR tv165[rsp]
mov edx, 1
lea rcx, QWORD PTR a1$[rsp]
call A::A(int,std::basic_string<char,std::char_traits<char>,std::allocator<char> >) ; A::A
npad 1
lea rdx, OFFSET FLAT:$SG35833
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
lea rdx, QWORD PTR a1$[rsp]
lea rcx, QWORD PTR a2$[rsp]
call A::A(A const &) ; A::A
npad 1
lea rdx, OFFSET FLAT:$SG35834
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
lea rcx, QWORD PTR a1$[rsp]
call A && std::move<A &>(A &) ; std::move<A &>
mov rdx, rax
lea rcx, QWORD PTR a3$[rsp]
call A::A(A &&) ; A::A
npad 1
lea rdx, OFFSET FLAT:$SG35835
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
lea rdx, QWORD PTR a3$[rsp]
lea rcx, QWORD PTR a2$[rsp]
call A & A::operator=(A const &) ; A::operator=
lea rdx, OFFSET FLAT:$SG35836
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
lea rcx, QWORD PTR a2$[rsp]
call A && std::move<A &>(A &) ; std::move<A &>
mov rdx, rax
lea rcx, QWORD PTR a3$[rsp]
call A & A::operator=(A &&) ; A::operator=
lea rdx, OFFSET FLAT:$SG35837
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
lea rcx, QWORD PTR a4$[rsp]
call A return_by_value(void) ; return_by_value
npad 1
lea rdx, OFFSET FLAT:$SG35838
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
lea rcx, QWORD PTR a3$[rsp]
call A & return_by_ref(A &) ; return_by_ref
mov QWORD PTR ref_a$[rsp], rax
mov rax, QWORD PTR ref_a$[rsp]
add rax, 8
mov QWORD PTR tv148[rsp], rax
lea rdx, OFFSET FLAT:$SG35840
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
mov rcx, QWORD PTR tv148[rsp]
mov rdx, rcx
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<char,std::char_traits<char>,std::allocator<char> >(std::basic_ostream<char,std::char_traits<char> > &,std::basic_string<char,std::char_traits<char>,std::allocator<char> > const &) ; std::operator<<<char,std::char_traits<char>,std::allocator<char> >
lea rdx, OFFSET FLAT:$SG35839
mov rcx, rax
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
lea rdx, OFFSET FLAT:$SG35841
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
lea rcx, QWORD PTR a3$[rsp]
call A && return_by_rvalue_ref(A &) ; return_by_rvalue_ref
mov rdx, rax
lea rcx, QWORD PTR a5$[rsp]
call A::A(A &&) ; A::A
npad 1
lea rdx, OFFSET FLAT:$SG35842
mov rcx, QWORD PTR __imp_std::basic_ostream<char,std::char_traits<char> > std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::operator<<<std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > &,char const *) ; std::operator<<<std::char_traits<char> >
npad 1
lea rcx, QWORD PTR a5$[rsp]
call A::~A(void) ; A::~A
npad 1
lea rcx, QWORD PTR a4$[rsp]
call A::~A(void) ; A::~A
npad 1
lea rcx, QWORD PTR a3$[rsp]
call A::~A(void) ; A::~A
npad 1
lea rcx, QWORD PTR a2$[rsp]
call A::~A(void) ; A::~A
npad 1
lea rcx, QWORD PTR a1$[rsp]
call A::~A(void) ; A::~A
npad 1
xor eax, eax
mov rcx, QWORD PTR __$ArrayPad$[rsp]
xor rcx, rsp
call __security_check_cookie
add rsp, 312 ; 00000138H
ret 0
main ENDP