代码意外地无法编译。为什么呢?

时间:2015-10-11 08:53:39

标签: c++ c++11 inheritance inner-classes move-constructor

查看以下代码示例,该示例使用与uncopiable类似的类boost::noncopyable

#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 () {}

由于我只想进行内部类移动,所以我明确地将其移动构造函数和赋值运算符指定为默认值,但也因为我听说在这种情况下指定所有“特殊成员函数”是一个好习惯我从uncopiable继承了它。问题是每个编译器都会编译失败,并显示类似于以下错误消息的内容(此消息摘自clang):

  

/ usr / include / c ++ / v1 / memory:1645:31:错误:调用'A :: B'的隐式删除的复制构造函数

     

...

     

main.cpp:26:10:注意:在实例化函数模板特化'std :: __ 1 :: vector&gt; :: emplace_back&lt;&gt;'请求

     

main.cpp:19:3:注意:复制构造函数被隐式删除,因为'B'有一个用户声明的移动构造函数

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

我的问题是:为什么会发生?是否可以认为通过继承辅助类来禁用构造函数/赋值运算符?

2 个答案:

答案 0 :(得分:4)

问题是您的uncopiable课程无法移动。因此,派生类的default移动构造函数/赋值运算符尝试使用delete d副本版本。

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

原因是§12.8¶9:

  

如果类X的定义未明确声明移动构造函数,则当且仅当

时,将隐式声明一个默认值。      
      
  • X没有用户声明的复制构造函数,
  •   
  • X没有用户声明的副本分配运算符
  •   
  • X没有用户声明的移动赋值运算符和
  •   
  • X没有用户声明的析构函数。
  •   

将复制运算符或赋值运算符声明为delete d仍然计为声明它。

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

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

请注意move operations should usually be declared noexcept。特别是如果你想在你的例子中使用std::vector中的类型。

答案 1 :(得分:0)

这在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();
}