我遇到了一种非常有趣的情况,因为我正在编译代码,尽管我很惊讶,所以我想问你的看法。
情况是这样的。我有一个带有删除的移动和复制构造函数的类,它具有用户定义的赋值操作符:
struct A {
A() { }
A(const A&) = delete;
A(A&& ) = delete;
A& operator=(const A& ) { return *this; }
A& operator=(A&& ) { return *this; }
};
我有另一个类,A
是唯一的成员。在本课程中,我定义了复制构造函数,但将move构造函数保留为默认值,并通过调用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?
我原来的答案错了,所以我重新来过。
(班上
。Copy],我们有:
一个默认的副本/类X的move构造函数定义为deleted(8.4.3),如果X具有:
- - - - - -[…]
-一个潜在构造的子对象类型M(或其数组)不能被复制/移动,因为重载解析(13.3)应用于M的相应构造函数时,会导致歧义或a被删除或从默认构造函数
中无法访问的函数- - - - - -[…)
该要点适用于B(B&& other) = default;
,因此move构造函数被定义为deleted。这似乎会破坏std::move()
的编译,但我们也有(通过缺陷1402的解决方案):
定义为deleted的默认move构造函数被重载解析(13.3, 13.4)忽略。(注意:被删除的move构造函数会干扰右值的初始化,右值可以使用而是复制构造函数。
忽略是关键。因此,当我们这样做时:
B b1;
B b2(std::move(b1));
即使删除了B
的移动构造函数,这段代码仍然是格式良好的,因为移动构造函数根本不参与重载解析,而是调用复制构造函数。因此,B
是MoveConstructible——即使你不能通过它的move构造函数来构造它。