我有这段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;
}
这不能编译,因为对象c1
和c2
的构造似乎涉及该类的move构造函数:
error: use of deleted function ‘MyClass::MyClass(MyClass&&)’
似乎编译器要创建临时对象,然后将其移动到c1
和c2
。为什么会这样呢?这三个语句不应该只调用MyClass(Args& a)
构造函数吗?
另一方面,如果我创建了move构造函数,则程序可以正常编译,并且永远不会调用move构造函数!!!
答案 0 :(得分:9)
请参见copy elision:
在以下情况下,允许编译器使用,但不需要编译器省略类对象的copy-和move-(自C ++ 11起)构造,即使复制/移动(自C ++ 11起)构造函数和析构函数具有明显的副作用。这是一种优化:即使发生并且未调用copy- / move-constructor,它也必须存在并且可以访问(好像根本没有优化发生),否则程序格式错误。
自C ++ 17起:
它们不必存在或不可访问,因为语言规则可确保即使在概念上也不会进行复制/移动操作。
答案 1 :(得分:6)
为什么会这样?这三个语句不应该只调用
MyClass(Args& a)
构造函数吗?
对于MyClass c1 = MyClass(a);
和MyClass c2 = a;
,临时MyClass
将首先由构造函数MyClass::MyClass(Args& a)
构造,然后用于复制初始化对象c1
和c2
。构造的临时变量是右值,这意味着将为复制初始化选择move-constructor。
另一方面,如果我创建了move构造函数,则程序可以正常编译,并且永远不会调用move构造函数!!!
原因是copy elision;复制/移动操作在此处被省略,导致使用MyClass::MyClass(Args& a)
直接初始化对象c1
和c2
的事实。
自C ++ 17起,有关复制删除的规则已更改。请注意,在C ++ 17之前的版本中,不能保证复制省略。对于非保证的复制省略,即使省略了复制/移动操作,复制/移动构造函数仍然需要存在并且可以访问。
这是一种优化:即使发生并且未调用copy- / move-constructor,它也必须存在并且可以访问(好像根本没有优化发生),否则程序格式错误。
在C ++ 17之后,您的代码可以正常工作。为了保证复制省略,不需要复制/移动构造函数存在或可访问。
在以下情况下,要求编译器省略 类对象的复制和移动构造,即使 复制/移动构造函数和析构函数具有明显的副作用。 由于语言规则可确保它们不存在或不可访问 即使在概念上也没有进行复制/移动操作:
在初始化中,如果初始值设定项表达式是prvalue,并且源类型的cv不合格版本与 目标的类,初始化器表达式用于 初始化目标对象:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
答案 2 :(得分:6)
一个主要问题是:
MyClass c1 = MyClass(a);
这将创建类型为MyClass
的临时对象,临时类型为右值。然后,它尝试使用临时对象 copy-construct c1
,或者如果您有移动构造器,则尝试 move-construct c1
。
在C ++ 11和C ++ 14中,不会为您的类自动生成复制构造函数(因为您有一个用户定义(即使已删除)的move-constructor),因此只有已删除的move -constructor可用。好吧,如果不删除它,它将可用,从而导致您的错误。