Оператор присваивания недоступен в производном классе
Оператор присваивания в базовом классе, по-видимому, недоступен в производном классе. Учитывая этот код:
#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.
Так что да, это возможно, но подвержено ошибкам и опасно, особенно когда речь идет о будущих модификациях подкласса.