我遇到了一个非常有趣的情况,因为我正在编写的代码编译,即使我很惊讶它也是如此,我想请你接受。
情况就是这样。我有一个删除了移动和复制构造函数的类,它有用户定义的赋值运算符:
struct A {
A() { }
A(const A&) = delete;
A(A&& ) = delete;
A& operator=(const A& ) { return *this; }
A& operator=(A&& ) { return *this; }
};
我还有另一个以A
为唯一成员的班级。在这个类中,我定义了复制构造函数,但我保持移动构造函数为默认值,并通过调用swap函数定义赋值运算符:
class B{
public:
A a;
B()
: a{}
{ }
B(const B&)
: a{}
{ }
B(B&& other) = default;
};
int main() {
B b1;
B b2(std::move(b1)); // compiles??
}
为什么默认移动构造函数工作,考虑到它不能简单地调用移动或复制构造函数A?我使用的是gcc 4.8.4。
答案 0 :(得分:7)
我原来的回答是错的,所以我重新开始。
在[class.copy]中,我们有:
违约副本/ 如果X具有:
,则将类X的移动构造函数定义为已删除(8.4.3) - [...]
- 可能构造的子对象类型M(或其数组),由于无法复制/移动 重载决策(13.3),应用于M的相应构造函数,导致模糊或a 已删除的功能或默认构造函数无法访问的功能,
- [...]
该子弹点适用于B(B&& other) = default;
,因此移动构造函数被定义为已删除。这似乎打破了std::move()
的编译,但我们也有(通过defect 1402的解析):
被定义为已删除的默认移动构造函数被重载决策忽略(13.3,13.4)。 [注意: 删除的移动构造函数否则会干扰rvalue的初始化,而rvalue可以使用 复制构造函数。 -end note]
忽略是关键。因此,当我们这样做时:
B b1;
B b2(std::move(b1));
即使删除B
的移动构造函数,此代码也是格式良好的,因为移动构造函数根本不参与重载解析,而是调用复制构造函数。因此,B
是MoveConstructible - 即使你不能通过它的移动构造函数构造它。