40

Until a test I've just made, I believed that only Constructors were not inherited in C++. But apparently, the assignment operator= is not too...

  1. What is the reason of that ?
  2. Is there any workaround to inherit the assignment operator ?
  3. Is it also the case for operator+=, operator-=, ... ?
  4. Are all other functions (apart from constructors/operator=) inherited ?

In fact, I encountered this problem as I was doing some CRTP :

template<class Crtp> class Base
{
    inline Crtp& operator=(const Base<Crtp>& rhs) {/*SOMETHING*/; return static_cast<Crtp&>(*this);}
};

class Derived1 : public Base<Derived1>
{
};

class Derived2 : public Base<Derived2>
{
};

Is there any solution to get that working ?

EDIT : OK, I have isolated the problem. Why the following isn't working ? How to solve the problem ?

#include <iostream>
#include <type_traits>

// Base class
template<template<typename, unsigned int> class CRTP, typename T, unsigned int N> class Base
{
    // Cast to base
    public:
        inline Base<CRTP, T, N>& operator()()
        {
            return *this;
        }

    // Operator =
    public:
        template<typename T0, class = typename std::enable_if<std::is_convertible<T0, T>::value>::type>
        inline CRTP<T, N>& operator=(const T0& rhs)
        {
            for (unsigned int i = 0; i < N; ++i) {
                _data[i] = rhs;
            }
            return static_cast<CRTP<T, N>&>(*this);
        }

    // Data members
    protected:
        T _data[N];
};

// Derived class
template<typename T, unsigned int N> class Derived : public Base<Derived, T, N>
{
};

// Main
int main()
{
    Derived<double, 3> x;
    x() = 3; // <- This is OK
    x = 3;   // <- error: no match for 'operator=' in ' x=3 '
    return 0;
}
Vincent
  • 50,257
  • 51
  • 171
  • 339
  • It is worth a read of item 45 of Scott Myers book (effecive C++) and also to not that sometime = does not mean = but copy constructor. – Ed Heal Aug 17 '12 at 16:56
  • your `Derived1` 's compiler generated `operator=` shadows base class's `operator=` – Mr.Anubis Aug 17 '12 at 16:59
  • @eq-: [yes it will](http://ideone.com/foOKK) – Mike Seymour Aug 17 '12 at 17:04
  • What exactly do you mean by "working"? Do you mean you want the base class operator available to use to assign derived-class objects from base-class objects? Or something else? An example of what you're trying to assign, and how, would be useful. – Mike Seymour Aug 17 '12 at 17:11
  • if you moto is to inherit base class's `operator=` then in derived class you could do `using Base::operator=` – Mr.Anubis Aug 17 '12 at 17:13
  • 1
    possible duplicate of http://stackoverflow.com/questions/4122214/why-operator-doesnt-get-inherited-from-a-template-class – baye Aug 17 '12 at 17:17
  • 1
    @lzprgmr exact duplicate, I would say. And there's a very good explanation and a workaround there. – Qnan Aug 17 '12 at 17:20

3 Answers3

41

The assignment operator is technically inherited; however, it is always hidden by an explicitly or implicitly defined assignment operator for the derived class (see comments below).

(13.5.3 Assignment) 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 a class if not declared by the user, a base class assignment operator is always hidden by the copy assignment operator of the derived class.

You can implement a dummy assignment operator which simply forwards the call to the base class operator=, like this:

// Derived class
template<typename T, unsigned int N> class Derived : public Base<Derived, T, N>
{
public:
    template<typename T0, class = typename std::enable_if<std::is_convertible<T0, T>::value>::type>
    inline Derived& operator=(const T0& rhs)
    {
        return Base<Derived, T, N>::operator=(rhs);
    }
};
eq-
  • 9,628
  • 34
  • 37
  • Okay. But so I have a really weird behaviour in my program. I will isolate the problem and edit my post. – Vincent Aug 17 '12 at 16:59
  • While technically correct, the inherited operator is hidden by the one declared (implicitly or explicitly) in the derived class. – Mike Seymour Aug 17 '12 at 17:02
  • @eq-: Your test calls the implicit `Derived::operator=`, which in turn calls `Base::operator=`. However, the base class version can't be used to assign a derived class object from a base class object, since it's hidden. [Demonstration](http://ideone.com/foOKK). I'm assuming that that's what the question is about, although it's currently a bit vague. – Mike Seymour Aug 17 '12 at 17:08
  • @eq : what would be the syntax of this dummy operator assignment ? – Vincent Aug 17 '12 at 17:36
13

The assignment operator is inherited, sort of, but... In any given class, if you do not provide a copy assignment operator, the compiler generates one for you. That means that your derived classes effectively have an assignment operator:

Derived& operator=( Derived const& );

And the usual hiding rules apply; this hides all of the base class assignment operators. (If the base class had an assignment operator with this signature, the derived class would inherit it normally.)

James Kanze
  • 142,482
  • 15
  • 169
  • 310
7
  1. Your assignment operator is technically inherited, but then it's hidden by the default copy assignment operator in the derived class. This default copy assignment then tries to call the base class's copy assignment which doesn't exist since you hid it with your own assignment.

  2. The sanest way to resolve this is to not use operator overloading in non-obvious ways (= not meaning copy assignment for example). In this case, don't use operator=: Call it something like assign or set and then it will inherit and not be hidden by the child copy assignment.

  3. These operators are inherited and there are no compiler versions so they will never be automatically hidden like operator=.

  4. It really is only constructors that aren't inherited, and I can't think of any other compiler-generated functions that could hide something from the parent as in operator=.

Mark B
  • 91,641
  • 10
  • 102
  • 179