【问题标题】:Assignment operator not available in derived class派生类中不可用的赋值运算符
【发布时间】:2021-07-31 06:14:20
【问题描述】:

基类中的赋值运算符在派生类中似乎不可用。鉴于此代码:

#include <iostream>

class A{
    int value;
public:
    A& operator=(int value){
        this->value = value;
        return *this;
    }
};

class B : public A{};

int main(){
    B b;
    b = 0; // Does not work
    return 0;
}

GCC 6.4 说:

错误:'operator=' 不匹配(操作数类型为 'B' 和 'int')

发生了什么?

【问题讨论】:

    标签: c++ inheritance assignment-operator


    【解决方案1】:

    为了使其工作,您需要将operator= 带入B 的范围:

    class B : public A
    {
    public:
    using A::operator=;
    };
    

    根据标准[class.copy.assign/8]:

    因为复制/移动赋值运算符是隐式声明的 如果用户未声明类,则为基类复制/移动分配 运算符总是被相应的赋值运算符隐藏 派生类 (16.5.3)。

    所以,因为 B::operator= 已被隐式声明,它隐藏了 A::operator=,如果你想使用它,则需要将它带入范围。

    进一步引用标准 [over.ass/1]

    赋值运算符应由非静态成员实现 函数只有一个参数。 因为副本分配 operator operator= 如果未声明,则为类隐式声明 由用户(15.8),基类赋值运算符始终隐藏 由派生类的复制赋值运算符。

    重点是我的。

    【讨论】:

      【解决方案2】:

      问题是编译器会为B类添加一个隐式赋值运算符,声明为

      B& operator=(const B&);
      

      此运算符会将运算符隐藏A,因此编译器不会知道它。

      解决方案是告诉编译器也使用 A 中的运算符和 using 关键字:

      class B : public A
      {
      public:
          using A::operator=;
      };
      

      【讨论】:

      • 这不只是因为B 隐藏A::operator= 中隐式声明的复制赋值吗?
      • @spectras 是的,这是真的,这就是我更新答案的原因(就在你发表评论时)。
      【解决方案3】:

      正如其他现有答案所指出的,B 隐式生成的赋值运算符隐藏了A 中定义的赋值运算符。对于基类中的任何非虚成员函数都是如此,这里唯一的特点是自动生成的赋值运算符。

      但首先要弄清楚你是否真的想这样做。想象一下你的类B 有需要以某种方式初始化的数据成员。使用来自A 的分配对这些数据成员有何影响? A 对其派生类数据成员一无所知,它们将保持不变。看看以下场景,其中赋值运算符通过 using 指令可用:

      class B : public A {
         public:
            using A::operator=;
      
            int m = 0; // Default-initialize data member to zero
      };
      
      B b;
      b.m = 42;
      b = 0; // Doesn't touch B::m... intended? A bug? Definitely weird.
      

      所以是的,这是可能的,但容易出错且很危险,尤其是在将来修改子类时。

      【讨论】:

      • 它的行为没有什么不同,任何其他方法都会出现同样的问题。
      • @spectras “同样的问题” ??通常方法由派生类继承,无需进一步措施即可使用。编译器不会隐式声明任何其他方法
      • @user463035818 这里的问题是名称隐藏。请参阅一些程序员老兄的回答。只要派生类中的方法与基类中的方法同名,您就会遇到这个问题^^
      • @spectras 好的,谢谢,现在我明白你的意思了,虽然 OP 没有在派生中声明 operator=,而且它在隐藏方面很特别
      • 最好的答案,imo,因为它解决了为什么隐藏赋值运算符是可取的,以及将其拖入的警告。
      【解决方案4】:

      当我们自己不提供时,每个类至少隐式定义了一个赋值运算符。

      当派生类中的成员函数被定义为与基类中的成员同名时,它会隐藏该名称的所有基类定义。

      您可以使用 using 声明,但请注意,它将拉出 所有名为 operator= 的成员并允许使用如下代码:

      A a;
      B b;
      b = a;
      

      这有点可疑。

      【讨论】:

      • 我实际上必须阅读Mark's answer 才能理解您的第二段,但是一旦我了解到您的回答非常有用。
      猜你喜欢
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      • 2020-02-16
      • 1970-01-01
      • 2012-06-05
      • 2016-07-10
      相关资源
      最近更新 更多