Оператор присваивания недоступен в производном классе

Оператор присваивания в базовом классе, по-видимому, недоступен в производном классе. Учитывая этот код:

#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

Каждый класс имеет по крайней мере один оператор присваивания, неявно определенный, когда мы сами его не предоставляем.

И когда функция-член в производном классе определяется с тем же именем, что и член в базовом классе, она скрывает все определения базового класса для этого имени.

Вы можете использовать объявление using, но имейте в виду, что оно вытянет все члены с именем operator= и разрешит код следующим образом:

A a;
B b;
b = a;

Что немного сомнительно.

Ответ 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]

Оператор присваивания должен быть реализован нестатической функцией-членом с ровно одним параметром. Поскольку оператор оператора копирования = неявно объявляется для класса, если он не объявлен пользователем (15.8), оператор назначения базового класса всегда скрыт оператором копирования копии производного класса.

Акцент мой.

Ответ 3

Проблема в том, что компилятор добавит оператор неявного присваивания для класса B, объявленного как

B& operator=(const B&);

Этот оператор скрывает оператор от A, поэтому компилятор не будет об этом знать.

Решение состоит в том, чтобы сообщить компилятору также использовать оператор из A с ключевым словом using:

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

Ответ 4

Как указано в других существующих ответах, неявно сгенерированный оператор присваивания 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.

Так что да, это возможно, но подвержено ошибкам и опасно, особенно когда речь идет о будущих модификациях подкласса.