派生类中不可用的赋值运算符

Posted

技术标签:

【中文标题】派生类中不可用的赋值运算符【英文标题】:Assignment operator not available in derived class 【发布时间】:2019-06-30 07:00:52 【问题描述】:

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

#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')

发生了什么?

【问题讨论】:

【参考方案1】:

正如其他现有答案所指出的,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,因为它解决了为什么隐藏赋值运算符是可取的,以及将其拖入的警告。【参考方案2】:

为了使其工作,您需要将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),基类赋值运算符始终隐藏 由派生类的复制赋值运算符。

重点是我的。

【讨论】:

【参考方案3】:

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

B& operator=(const B&);

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

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

class B : public A

public:
    using A::operator=;
;

【讨论】:

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

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

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

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

A a;
B b;
b = a;

这有点可疑。

【讨论】:

我实际上必须阅读Mark's answer 才能理解您的第二段,但是一旦我了解到您的回答非常有用。

以上是关于派生类中不可用的赋值运算符的主要内容,如果未能解决你的问题,请参考以下文章

重载赋值运算符 - 多态容器

派生类继承基类赋值运算符?

派生赋值运算符从基础调用

C++的探索路12继承与派生之高级篇--派生类与赋值运算符及多重继承

如何从基类调用派生赋值运算符?

Fortran 派生类型:重载赋值运算符不适用于“参数”属性