Наследование родительского оператора присваивания при неявном удалении потомка

В GCC 4.6 возможно наследовать операторы присваивания родителя, даже если дочерние операторы присваивания неявно удалены из-за конструктора перемещения. В более поздних версиях GCC (а также Clang) это больше невозможно. Как правильно, чтобы дочерний класс использовал операторы присваивания родителя?

struct A
{
  A & operator=(A const & other) = default;
};

struct B : public A
{
  B() {}
  B(B && other) {}

  using A::operator=;
};

int main()
{
  B b1, b2;
  b1 = b2; // error: use of deleted function because B's operator= is implicitly deleted due to move constructor
  return 0;
}

2 ответа

Решение

Удаленная функция по-прежнему объявляется, удаляется только определение. Расширяя это в вашем определении класса:

struct B : A {
   using A::operator=;               // A& operator=(const A&)
   B& operator=(const B&) = delete;
};

На данный момент, вы можете заметить, что есть две декларации для operator= в производном типе первый (введенный в область видимости с помощью объявления- использования) принимает const A& аргумент, в то время как второй принимает const B& и удаляется.

Когда вы позже попробуйте назначение:

B b1, b2;
b1 = b2;

Оба объявления видятся компилятором, а второе лучше подходит. Поскольку он помечен как удаленный, вы получите ошибку. Если у вас есть, с другой стороны, назначен A Объект, который бы работал, как ожидалось:

B b1, b2;
b1 = static_cast<A&>(b2); // works, BUT...

Проблема этого подхода заключается в том, что он копирует только базовые подобъекты, что, вероятно, не то, что вам нужно. Если вы просто хотите получить такое же поведение, как если бы присваивание было сгенерировано компилятором, вам нужно запросить его:

struct B : A {
   // ...
   B& operator=(const B&) = default;
};

Это будет зависеть от того, что вы хотите, чтобы произошло, когда вы назначаете производный тип для себя. Если вы хотите, чтобы дочерний оператор присваивания работал как "обычный", несмотря на то, что оператор перемещения подавляет неявное генерирование, вы можете просто вернуть дочернее присваивание обратно в класс, используя это:

    B &operator=( B const & ) = default;

Это, вероятно, будет эквивалентно тому, что сделал GCC 4.6. Я полагаю, что GCC 4.6 должным образом не подавляет сгенерированные операторы, как того требует стандарт, поэтому вы, вероятно, просто получали обычное поведение оператора присваивания вместе с любыми перегрузками из базового класса, которые использовались в объявлении using.

Если вы на самом деле хотите назначить только базовую часть класса, вам нужно реализовать свой собственный оператор присваивания:

    B &operator=( B const &that ) {
        static_cast<A&>(*this) = that;
        return *this;
    }

К сожалению, у меня нет GCC 4.7, чтобы попробовать его прямо сейчас, но я не удивлюсь, если вы действительно получаете оператор присваивания базового класса в своем производном классе, но оператор удаленного присваивания производного класса лучше подходит для вашего примера, Вы можете проверить это, попробовав эту строку в вашем main():

    b1 = static_cast<A const&>(b2);

Другие вопросы по тегам