隐式删除子项时继承父级赋值运算符

时间:2013-04-01 21:58:54

标签: c++ inheritance c++11 move using-declaration

在GCC 4.6中,即使由于移动构造函数而隐式删除了子节点的赋值运算符,也可以继承父节点的赋值运算符。在GCC的后续版本(以及Clang)中,这已不再可能。让子类使用父类赋值运算符的正确方法是什么?

struct A
{
  A & operator=(A const & other) = default;
};

struct B : public A
{
  B() {}
  B(B && other) {}

  using A::operator=;
};

int main()
{
  B b1, b2;
  b1 = b2; // error: use of deleted function because B's operator= is implicitly deleted due to move constructor
  return 0;
}

2 个答案:

答案 0 :(得分:6)

删除的功能仍然是声明,只删除定义。在类定义中扩展它:

struct B : A {
   using A::operator=;               // A& operator=(const A&)
   B& operator=(const B&) = delete;
};

此时,您可以注意到派生类型中有operator=的两个声明,第一个声明(通过 using-declaration 进入范围)需要const A&参数,而第二个参数需要const B&并且已删除

以后尝试分配时:

B b1, b2;
b1 = b2;

编译器可以看到这两个声明,第二个是更好的匹配。因为它被标记为已删除,您会收到错误消息。另一方面,如果您有一个A对象,它将按预期工作:

B b1, b2;
b1 = static_cast<A&>(b2); // works, BUT...

这种方法的问题在于它只复制了可能不是你想要的基础子对象。如果您只想要与编译器生成赋值所需的相同行为,则需要请求它:

struct B : A {
   // ...
   B& operator=(const B&) = default;
};

答案 1 :(得分:2)

这取决于您为自己分配派生类型时想要发生的事情。如果你希望子赋值运算符像“normal”一样工作,尽管move运算符可以抑制隐式生成,你可以简单地将子赋值带回到类中,使用:

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

这可能与GCC 4.6所做的相同。我相信GCC 4.6并没有像标准要求那样正确地抑制生成的运算符,所以你可能只是获得了正常的赋值运算符行为,以及你的using声明引入的基类的任何重载。

如果您实际上只想分配类的基础部分,则需要实现自己的赋值运算符:

    B &operator=( B const &that ) {
        static_cast<A&>(*this) = that;
        return *this;
    }

不幸的是我现在没有GCC 4.7试用,但是如果您实际上在派生类中获得基类赋值运算符,我不会感到惊讶,但派生类的已删除赋值运算符是更好的匹配为你的例子。您可以通过在main()中尝试此行来测试它:

    b1 = static_cast<A const&>(b2);