3

在 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;
}
4

2 回答 2

6

被删除的函数仍然被声明,只是定义被删除。在您的类定义中扩展它:

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

此时,您可以注意到在派生类型中有两个声明,第一个(通过using-declarationoperator=进入范围)接受一个参数,而第二个接受一个并被删除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;
};
于 2013-04-01T22:19:55.350 回答
2

当您将派生类型分配给自身时,这将取决于您希望发生的事情。如果您希望子赋值运算符像“正常”一样工作,尽管移动运算符抑制了隐式生成,您可以简单地将子赋值带回类中,使用以下方法:

    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);

于 2013-04-01T22:22:56.010 回答