【问题标题】:Automatically generated move constructor with not movable members自动生成具有不可移动成员的移动构造函数
【发布时间】:2015-08-24 15:43:16
【问题描述】:

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

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

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

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

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

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。

【问题讨论】:

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


    【解决方案1】:

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


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

    默认副本/ 如果 X 具有以下属性,则将类 X 的移动构造函数定义为已删除 (8.4.3):
    — [...]
    — 一个潜在构造的子对象类型 M(或其数组),不能被复制/移动,因为 应用于 M 的相应构造函数的重载决议 (13.3) 会导致歧义或 已删除或无法从默认构造函数访问的函数,
    — [...]

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

    定义为已删除的默认移动构造函数被重载决议忽略(13.3、13.4)。 [注: 否则,删除的移动构造函数会干扰从右值初始化,该右值可以使用 而是复制构造函数。 ——尾注]

    忽略是关键。因此,当我们这样做时:

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

    即使B 的移动构造函数被删除,这段代码的格式也很好,因为移动构造函数根本不参与重载决议,而是调用了复制构造函数。因此,B 是可移动构造的 - 即使您无法通过其移动构造函数构造它。

    【讨论】:

    • 我在复制和赋值中添加了几个 couts,这似乎证实了您的假设,即无法正确编译移动构造函数,它只是恢复到复制。我不是标准细节方面的专家,但看起来默认声明的行为类似于模板中的 SFINAE
    • 定义为已删除的默认移动构造函数被重载决议忽略。见 N4527 [class.copy]/p11;还有N3667
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-07
    • 1970-01-01
    • 2014-11-08
    • 2012-11-09
    • 2019-04-16
    • 2021-12-23
    相关资源
    最近更新 更多