我的自定义实现的Integer类如下:
org.gradle.jvmargs=-XX\:MaxHeapSize\=2048m -Xmx4608M
move构造函数的实现如下:
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;
}
我希望有一个参数化的构造函数(用于临时对象),然后移动要调用的构造函数。但是未调用move构造函数。
在拆卸时,我得到了如下所示的东西:
Integer obj2{ Integer{5}};
我想这是实际的返回值优化(RVO)。 我说的对吗?
由于大多数编译器都实现了RVO,所以我不应该这样做
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
我应该吗?
答案 0 :(得分:2)
这是一个棘手的问题,因为它在c ++ 17中进行了技术更改。在c ++ 11中,这是NRVO优化,但是在c ++ 17中,它甚至不再是优化。
cppreference的相关摘录:
在以下情况下,即使复制/移动构造函数和析构函数具有明显的副作用,也要求编译器省略类对象的复制和移动构造。这些对象直接构造到存储中,否则会将它们复制/移动到其中。 复制/移动构造函数不需要存在或不可访问,因为语言规则确保即使在概念上也不会进行复制/移动操作:
[...]
在变量的初始化中,当初始化器表达式是与变量类型相同的类类型(忽略cv限定)的prvalue时:
T x = T(T(f())); // only one call to default constructor of T, to initialize x
注意:上面的规则未指定优化:prvalues和temporaries的C ++ 17核心语言规范与早期C ++修订版的根本不同:不再需要复制或移动的临时属性。描述C ++ 17机制的另一种方法是“未实现的值传递”:prvalue的返回和使用无需实现临时值。
重点是我的重点。上段自c ++ 17起就已存在,而在c ++ 11中不存在。
现在,c ++ 11:
在以下情况下,允许编译器,但即使复制/移动(自C ++ 11起)构造函数和析构函数也不需要省略类对象的复制和移动(自C ++ 11起)构造有明显的副作用。这些对象直接构造到存储中,否则会将它们复制/移动到其中。 这是一种优化:即使发生并且未调用copy / move(自C ++ 11起)构造函数时,它仍然必须存在并且可以访问(就像没有发生优化一样)完全没有),否则程序格式不正确:
[...]
在对象的初始化中,当源对象是无名的临时对象且与目标对象具有相同的类类型(忽略cv限定)时。当无名临时变量是return语句的操作数时,复制省略的这种变体称为RVO,即“返回值优化”。 (直到c ++ 17)
这是您的情况。因此对于c ++ 11,它是用于初始化的RVO。 return
语句RVO实际上被另一个项目符号覆盖。
答案 1 :(得分:2)
就像您自己想出的那样,由于RVO而没有调用move构造函数。
请注意,std::move
只是将其参数转换为右值引用。在您的示例中,Integer{5}
是一个未命名的临时变量,已经是一个右值。因此,不需要额外调用std::move
。如果未如您所愿地完全删除了move构造函数,那么它将被调用。
另外,请注意,在move构造函数的实现中,多余的std::move
是不必要的,因为ptr_int_
是原始指针,没有任何特殊的move语义。