Search code examples
c++templatesinheritanceassignment-operator

C++: Inheriting the const assignment operator defined in the base class for the derived type


Many, many, many posts about inheriting operator=, with the usual answer being "it's overshadowed by the implicitly defined one, use using Base::operator= if you know what you're doing".

Here's a hopefully slightly different question. Consider the following in which base class A knows about the exact type of the derived class:

#include <iostream>

template <typename E>
struct A {
    E& operator= (const E& other) {
      std::cout << "A<E>::op=\n";
      return *(E*)this;
    }

    E& operator= (E& other) {
      std::cout << "A<E>::op no const=\n";
      return *(E*)this;
    }
};

struct B : public A<B> {
    using A<B>::operator=;
};

int main () {
  B x, y;
  x = y;
  const B z;
  x = z;
}

In the main function, when calling x = y, the method E& A<E>::operator= (E& other) with E = B is called, as expected. However, x = z does not call E& A<E>::operator= (const E& other).

Why? (Note that this is a question about behavior, not style :-) )


Solution

  • Why?

    Because x = z uses the implicitly defined

    B& B::operator=(const B&)
    

    and the implicitly defined

    A& A::operator=(const A&)
    // instantiated as:
    A<B>& A<B>::operator=(const A<B>&)
    

    The implicit definitions are not removed just because you bring the base class operators out of hiding. See [class.copy]/24:

    A using-declaration ([namespace.udecl]) that brings in from a base class an assignment operator with a parameter type that could be that of a copy/move assignment operator for the derived class is not considered an explicit declaration of such an operator and does not suppress the implicit declaration of the derived class operator; the operator introduced by the using-declaration is hidden by the implicitly-declared operator in the derived class.

    The reason why the other case, x = y, instead uses E& A::operator= (E& other) is because it's a better match since it doesn't need to convert y to a const&.