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

时间:2015-08-24 15:43:17

标签: c++ c++11 move-semantics

我遇到了一个非常有趣的情况,因为我正在编写的代码编译,即使我很惊讶它也是如此,我想请你接受。

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

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。

1 个答案:

答案 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 - 即使你不能通过它的移动构造函数构造它。