c++ - 未按预期调用移动构造函数
问题描述
我自定义实现的整数类如下:
class Integer
{
private:
int * ptr_int_; // Resource
public:
// Other ctors
Integer(Integer &&); // Move constructor
// dtor
}
移动构造函数实现如下:
Integer::Integer(Integer && arg)
{
std::cout << "Integer(Integer && arg) called\n";
this->ptr_int_ = arg.ptr_int_; // Shallow Copy
arg.ptr_int_ = nullptr;
}
在我的驱动程序中,对于以下调用,
Integer obj2{ Integer{5}};
我期望一个参数化的构造函数(用于临时对象),然后移动构造函数被调用。但是没有调用移动构造函数。
在反汇编中,我得到了如下所示的东西:
Integer obj2{ Integer{5}};
001E1B04 push 4
001E1B06 lea ecx,[obj2]
001E1B09 call Integer::__autoclassinit2 (01E1320h)
001E1B0E mov dword ptr [ebp-114h],5
001E1B18 lea eax,[ebp-114h]
001E1B1E push eax
001E1B1F lea ecx,[obj2] ;; Is this copy elision(RVO) in action?
001E1B22 call Integer::Integer (01E12FDh)
001E1B27 mov byte ptr [ebp-4],1
我想,这就是实际的返回值优化(RVO)。我对吗?
由于大多数编译器都实现了 RVO,我不应该这样做
Integer obj2{ std::move(Integer{5})};
我是不是该?
解决方案
这是一个棘手的问题,因为它在 c++17 中在技术上发生了变化。在 c++11 中它是 NRVO 优化,但在 c++17 中它甚至不再是优化。
- 您不应该期望移动 c'tor,这取决于编译器。
- 由于 c++17 你不能指望它,它不能被调用。
cppreference的相关摘录:
在以下情况下,编译器必须省略类对象的复制和移动构造,即使复制/移动构造函数和析构函数具有可观察到的副作用。对象直接构建到存储中,否则它们将被复制/移动到。复制/移动构造函数不需要存在或可访问,因为语言规则确保不会发生复制/移动操作,即使在概念上也是如此:
[...]
在变量的初始化中,当初始化表达式是与变量类型相同的类类型(忽略 cv-qualification)的纯右值时:
T x = T(T(f())); // only one call to default constructor of T, to initialize x
注意:上述规则未指定优化:纯右值和临时值的 C++17 核心语言规范与早期 C++ 修订版根本不同:不再有临时复制/移动。描述 C++17 机制的另一种方式是“未实现的值传递”:在没有实现临时值的情况下返回和使用纯右值。
重点是我的重要部分。上述段落自 c++17 以来就已存在,并且在 c++11 中不存在。
现在,c++11:
在以下情况下,编译器允许但不需要省略类对象的复制和移动(C++11 起)构造,即使复制/移动(C++11 起)构造函数和析构函数具有可观察的一面-效果。对象直接构建到存储中,否则它们将被复制/移动到。这是一种优化:即使它发生并且没有调用复制/移动(C++11 起)构造函数,它仍然必须存在且可访问(就好像根本没有发生优化一样),否则程序会出错-形成:
[...]
在对象的初始化中,当源对象是无名临时对象并且与目标对象具有相同的类类型(忽略 cv-qualification)时。当无名临时变量是 return 语句的操作数时,这种复制省略的变体称为 RVO,“返回值优化”。(直到 c++17)
这是你的情况。因此对于 c++11,它是用于初始化的 RVO。return
声明 RVO 实际上被另一个项目符号覆盖。
推荐阅读
- github - 如何将公共 ssh 密钥添加到私人仓库?
- c# - 如何连接来自 [Route] 和 CRUD 操作标头的路由字符串
- html - 如何摆脱电子邮件模板中的 html 表格边框
- sql - 将 UNION all 与具有变量赋值的查询一起使用时出错
- jenkins - 如何获取特定节点中存在的所有项目的列表?
- c# - 如何将小数点放在整数中
- python - DOCPLEX:添加一个对两个变量求和的约束
- java - 在Java中正确地将对象数组输出到.csv文件
- node.js - 当我将十六进制转换为 ascii 字符时如何获得正确的值
- c# - 如何为新创建的 Workspace 指定 TargetFramework?