首先我粗浅的认为同时存在没有问题,因为
- 一个是创建一个变量副本
- 一个转移一个生存周期将到的变量所申请的资源
环境
g++.exe (Rev2, Built by MSYS2 project) 9.2.0
g++.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
实验
sf 抽风,源码见 http://code.bulix.org/k07gcm-891664
A( const A& c )
& A( A&& c )
同时存在的输出
this: 0x66f880 @ A()0
申请内存:0x732760
this: 0x737d50 @ A( A&& c )0
移动内存: 0x732760
this: 0x66f880 @ ~A()0
被移动内存: 0
this: 0x66f880 @ A()1
申请内存:0x737d70
this: 0x737dc0 @ A( A&& c )1
移动内存: 0x737d70
this: 0x737db0 @ A( const A& c )99 // ???
this: 0x737d50 0
申请内存:0x737de0
this: 0x737d50 @ ~A()0
释放内存: 0x732760
this: 0x66f880 @ ~A()1
被移动内存: 0
over
0x737db0 : 99 // ???
0x737dc0 : 1
this: 0x737db0 @ ~A()99
释放内存: 0x737de0
this: 0x737dc0 @ ~A()1
释放内存: 0x737d70
可见 调用了拷贝构造函数,这是不是内部没有使用 std::move 导致调用拷贝构造函数?(瞎猜)
只有 A( A&& c )
时的输出
this: 0x66f880 @ A()0
申请内存:0x1f2760
this: 0x1f7d50 @ A( A&& c )0
移动内存: 0x1f2760
this: 0x66f880 @ ~A()0
被移动内存: 0
this: 0x66f880 @ A()1
申请内存:0x1f7d70
this: 0x1f7dc0 @ A( A&& c )1
移动内存: 0x1f7d70
this: 0x1f7db0 @ A( A&& c )0
移动内存: 0x1f2760
this: 0x1f7d50 @ ~A()0
被移动内存: 0
this: 0x66f880 @ ~A()1
被移动内存: 0
over
0x1f7db0 : 0
0x1f7dc0 : 1
this: 0x1f7db0 @ ~A()0
释放内存: 0x1f2760
this: 0x1f7dc0 @ ~A()1
释放内存: 0x1f7d70
问题 2
class A a( 2 );
vect.push_back( std::move( a ) );
输出
this: 0x66f880 @ A()0
申请内存:0x1f2760
this: 0x1f7d50 @ A( A&& c )0
移动内存: 0x1f2760
this: 0x66f880 @ ~A()0
被移动内存: 0
这个应该是 push_back 的源码
push_back(value_type&& __x)
{ emplace_back(std::move(__x)); }
为什么只调一次移动构造函数,被优化了?
+------------------------------+
| 在 这 里 创 建 变 量 |
+------------------------------+
vect.push_back( std::move( a ) );
+------------------------------+
| 调 用 移 动 构 造 函 数 |
+------------------------------+
emplace_back(std::move(__x)); // <<<
+------------------------------+
| push_back 内 部 也 有 move |
| 为 什 么 不 会 再 次 调 用 移 动 构 造 |
| 函 数 |
+------------------------------+
1)
std::vector::push_back
有一个很强的异常安全保证,在元素类型可以拷贝构造的情况下,那么如果push_bach
抛出任何异常,那么std::vector
的原始状态不变。vector.modifiers/1
push_back
可能引起内存分配(原来的内存不够了),这时原来的元素会拷贝/移动。如果使用移动构造来移动这些元素,那么,万一移动构造抛出异常,源对象和新对象的状态都已经被破坏了,无法保证异常安全性。所以有拷贝的时候,除非可以确保移动构造不抛出异常,否则只会使用拷贝构造函数。没有拷贝构造函数时,只能使用移动构造函数。除非可以确保移动构造函数不抛出异常,否则不提供异常安全保证。
可以在移动构造函数上加上
noexcept
,保证其不抛出异常,这样就会使用移动构造函数了。见 https://www.ideone.com/9pyHdc 。2) 这个叫 copy elision。
class.copy/31
copy elision
我的另一个关于 copy elision 的回答