自动生成的移动构造函数,成员不可移动

Automatically generated move constructor with not movable members

我遇到了一个非常有趣的情况,因为我正在编写的代码可以编译,尽管我很惊讶它确实如此,所以我想请你接受。

情况是这样的。我有一个 class 删除了移动和复制构造函数,它有用户定义的赋值运算符:

struct A {
    A() { }
    A(const A&) = delete;
    A(A&& ) = delete;

    A& operator=(const A& ) { return *this; }
    A& operator=(A&& ) { return *this; }
};

我还有一个 class,A 是唯一的成员。在此 class 中,我定义了复制构造函数,但我将移动构造函数保留为默认值,并通过调用交换函数定义了赋值运算符:

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.

我原来的回答是错误的,所以我重新开始。


在 [class.copy] 中,我们有:

A defaulted copy/ move constructor for a class X is defined as deleted (8.4.3) if X has:
— [...]
— a potentially constructed subobject type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
— [...]

该要点适用于 B(B&& other) = default;,因此移动构造函数被定义为已删除。这似乎会破坏 std::move() 的编译,但我们也有(通过 defect 1402 的解析):

A defaulted move constructor that is defined as deleted is ignored by overload resolution (13.3, 13.4). [ Note: A deleted move constructor would otherwise interfere with initialization from an rvalue which can use the copy constructor instead. —end note ]

无视是关键。因此,当我们这样做时:

B b1;
B b2(std::move(b1));

尽管删除了 B 的移动构造函数,但这段代码的格式是正确的,因为移动构造函数根本不参与重载决策,而是调用了复制构造函数。因此,B 是 MoveConstructible - 即使您不能通过其移动构造函数构造它。