代码意外编译失败。为什么?

Code unexpectedly fails to compile. Why?

看看下面的代码示例,它使用类似于 boost::noncopyable 的 class uncopiable

#include <vector>

class uncopiable {
    using self = uncopiable;
protected:
    uncopiable() {}
    ~uncopiable() {}
    uncopiable(const self&) = delete;
    self& operator=(const self&) = delete;
};

struct A {
    struct B : uncopiable {
        using self = B;

        B() {
        }

        B(B&&) = default;
        self& operator=(B&&) = default;

        ~B() {
        }
    };

    A() { v.emplace_back(); }
    ~A() {}

private:
    std::vector<B> v;
};

int main () {}

因为我只想让内部 class 移动,所以我明确指定它的移动构造函数和赋值运算符为默认值,而且因为我听说指定所有 "special member functions" 在这种情况下我从 uncopiable 继承了它。问题是每个编译器的编译都失败,并显示类似于以下错误消息的内容(此消息摘自 clang 消息):

/usr/include/c++/v1/memory:1645:31: error: call to implicitly-deleted copy constructor of 'A::B'

...

main.cpp:26:10: note: in instantiation of function template specialization 'std::__1::vector >::emplace_back<>' requested here

main.cpp:19:3: note: copy constructor is implicitly deleted because 'B' has a user-declared move constructor

可以通过删除继承来修复它(仍然不会创建复制操作)。但是在那之后在 class 中写入要显式删除的复制操作也是可以的。

我的问题是:为什么会这样?通过继承 helper classes 来禁用 constructors/assignment 运算符是否可以被认为是一个缺陷?

问题是你的uncopiableclass不能移动。因此派生的 class 的 default 移动构造函数/赋值运算符尝试使用 deleted 复制版本。

static_assert(std::is_move_constructible<uncopiable>::value, "");  // fails
static_assert(std::is_move_assignable<uncopiable>::value, "");     // fails

原因是 § 12.8 ¶ 9:

If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if

  • X does not have a user-declared copy constructor,
  • X does not have a user-declared copy assignment operator,
  • X does not have a user-declared move assignment operator, and
  • X does not have a user-declared destructor.

将复制运算符或赋值运算符声明为 deleted 仍然算作声明。

解决方案当然是为uncopiable声明移动操作。

uncopiable(uncopiable&&) noexcept = default;
uncopiable& operator=(uncopiable&&) noexcept = default;

注意move operations should usually be declared noexcept。特别是如果您想像示例中那样在 std::vector 中使用类型。

这在 MinGw 上编译正常:

#include <vector>

class uncopiable {
    using self = uncopiable;
protected:
    uncopiable() {}
    ~uncopiable() {}
    uncopiable(const self&) = delete;
    self& operator=(const self&) = delete;
};

struct A {
    struct B : uncopiable {
        using self = B;

        B() {
        }

        B(B&&) {};
        self& operator=(B&&) = default;

        ~B() {
        }
    };

    A() { v.emplace_back(); }
    ~A() {}

private:
    std::vector<B> v;
};

int main () {
    A* a = new A();
}