ska*_*oto 8 c++ initialization copy-elision c++11
我有这段C++代码:
class Args {};
class MyClass {
public:
MyClass(Args& a) {}
MyClass(MyClass &&) = delete;
};
int main() {
Args a;
MyClass c1 = MyClass(a);
MyClass c2 = a;
MyClass c3(a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这不会编译,因为对象的构造c1和c2似乎涉及到类的移动构造函数:
error: use of deleted function ‘MyClass::MyClass(MyClass&&)’
似乎编译器想要创建临时对象,然后将它们移动到c1和c2.为什么会这样?不应该所有三个语句都只调用MyClass(Args& a)构造函数吗?
另一方面,如果我创建移动构造函数,程序编译正常,移动构造函数永远不会被调用!
在下列情况下,允许编译器,但不要求省略复制和移动(自C++ 11)类对象的构造,即使复制/移动(自C++ 11)构造函数和析构函数有可观察到的副作用.这是一个优化:即使它发生并且没有调用copy-/move-构造函数,它仍然必须存在并且可访问(好像根本没有发生优化),否则程序就会形成错误.
从C++ 17开始:
它们不需要存在或可访问,因为语言规则确保不进行复制/移动操作,即使在概念上也是如此.
为什么会这样?不应该所有三个语句都只调用
MyClass(Args& a)构造函数吗?
对于这两个MyClass c1 = MyClass(a);和MyClass c2 = a;,临时MyClass将首先由构造构成MyClass::MyClass(Args& a),则用于复制初始化对象c1和c2.构造的临时值是rvalues,这意味着将选择move-constructor进行复制初始化.
另一方面,如果我创建移动构造函数,程序编译正常,移动构造函数永远不会被调用!
原因是复制省略 ; 这里省略了复制/移动操作,导致MyClass::MyClass(Args& a)用于初始化对象c1和c2直接的事实.
关于复制省略的规则在C++ 17之后发生了变化.请注意,在C++之前的版本17中,不保证复制省略.对于无保证的复制省略,即使省略复制/移动操作,复制/移动构造函数仍然需要存在且可访问.
这是一个优化:即使它发生并且没有调用copy-/move-构造函数,它仍然必须存在并且可访问(好像根本没有发生优化),否则程序就会形成错误.
在C++ 17之后,您的代码可以正常工作.对于保证的复制省略,复制/移动构造函数不需要存在或可访问.
在下列情况下,编译器需要省略类对象的复制和移动构造,即使复制/移动构造函数和析构函数具有可观察到的副作用.它们不需要存在或可访问,因为语言规则确保不会发生复制/移动操作,甚至在概念上:
在初始化中,如果初始化表达式是prvalue且源类型的cv-nonqualified版本与目标类相同,则初始化表达式用于初始化目标对象:
Run Code Online (Sandbox Code Playgroud)T x = T(T(T())); // only one call to default constructor of T, to initialize x
一个主要问题是:
MyClass c1 = MyClass(a);
Run Code Online (Sandbox Code Playgroud)
这将创建一个类型的临时对象MyClass,临时类型是rvalues.然后它尝试使用临时对象进行复制构造 c1,或者如果您有移动构造函数则使用move-construct c1.
在C++ 11和C++ 14中,您的类没有自动生成的复制构造函数(因为您有一个用户定义的(即使已删除)移动构造函数),因此只有已删除的移动构造函数是可用.好吧,如果它没有被删除就可用,导致你的错误.