赋值运算符在派生 class 中不可用

Assignment operator not available in derived class

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

#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 说:

error: no match for 'operator=' (operand types are 'B' and 'int')

发生了什么事?

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

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

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

Because a copy/move assignment operator is implicitly declared for a class if not declared by the user, a base class copy/move assignment operator is always hidden by the corresponding assignment operator of a derived class (16.5.3).

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

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

An assignment operator shall be implemented by a non-static member function with exactly one parameter. Because a copy assignment operator operator= is implicitly declared for a class if not declared by the user (15.8), a base class assignment operator is always hidden by the copy assignment operator of the derived class.

重点是我的。

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

B& operator=(const B&);

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

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

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

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

但首先要弄清楚你是否真的想这样做。假设您的 class B 有需要以某种方式初始化的数据成员。使用来自 A 的赋值如何影响这些数据成员? A 对其派生的 class 数据成员一无所知,它们将保持不变。查看以下场景,其中赋值运算符已通过 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.

所以是的,这是可能的,但容易出错且危险,尤其是在涉及到子的未来修改时class。

每个 class 至少有一个赋值运算符隐式定义,而我们自己没有提供。

并且当派生 class 中的成员函数定义为与基 class 中的成员同名时,它会隐藏该名称的所有基 class 定义.

您可以使用 using 声明,但要注意它会提取 所有 名为 operator= 的成员并允许这样的代码:

A a;
B b;
b = a;

这有点可疑。