我有以下代码:
#include <iostream>
class B{
public:
//this should all be generated by default but
//throwing them in here just to be sure
B() = default;
B(const B& b) = default;
B& operator=(const B& b) = default;
B(B&& b) = default;
B& operator=(B&& b) = default;
};
class A {
public:
A(B x) : x_(x) {}
A(const A& a) = delete;
A& operator=(const A& a) = delete;
//move operations should be generated by compiler?
private:
B x_;
};
int main() {
A a = A(B());
}
我希望编译它并使用它的移动构造函数创建A
,但是这会失败并显示以下消息:
错误:使用已删除的函数'A :: A(const A&amp;)' A = A(B()); 注意:在此声明 A(const A&amp; a)=删除;
当然,添加移动操作并使用默认关键字标记它们可以解决问题。我应该假设移动操作不是由编译器生成的,为什么会这样? delete关键字是否将方法标记为用户实现,因此不会生成移动操作?为什么复制构造函数更喜欢?我正在使用gcc编译。
答案 0 :(得分:4)
如果您提供复制构造函数/ operator=
的实现,则默认情况下不再生成的移动操作。如果你想要它们,你需要明确告诉你这样做。
根据cppreference:
如果没有为类类型提供用户定义的移动构造函数 (struct,class或union),以下所有内容均为真:
- 没有用户声明的副本构造函数;
- 没有用户声明的副本赋值运算符;
- 没有用户声明的移动赋值运算符;
- 没有用户声明的析构函数;
然后编译器会将移动构造函数声明为非显式 具有签名
T::T(T&&)
的类的内联公共成员。
以下是针对此类情况的有用图表:
答案 1 :(得分:2)
说= delete
你不只是说代码不存在,而是如果代码试图调用你应该得到编译错误。
更具体地说,delete
d成员不是简单地从可用选项集中删除的:它们被搜索,如果它们匹配,则会出现编译错误。换句话说,= delete
适用于签名,当匹配意味着使用问题,而不是签名时,您不想匹配。
例如delete
d个成员do indeed participate in overload resolution。
提供= delete
与提供实现完全相同,区别在于如果编译器最终生成将调用已删除函数的代码,则会出现编译错误。如果您提供delete
d复制构造函数,则仍提供用户定义的复制构造函数。
您应该注意的另一个细微之处是移动构造函数/赋值的自动生成取决于例如“用户定义的复制构造函数”的存在以及该定义作为严格意义。一些在逻辑上不明显的东西(至少不适合我)是与复制构造函数的签名匹配的模板不是复制构造函数。换句话说:
struct Foo {
template<typename T>
Foo(const T& other) { ... }
};
模板可以匹配Foo(const Foo&)
但不被视为用户定义的复制构造函数(不要寻找深层原因,这是因为标准这样说)因此你仍然得到隐式生成的移动构造函数。如果默认实现不正确,这将是严重问题的根源......