Search code examples
c++inheritancecrtpcopy-assignment

CRTP & copy/move assignment/constructor inheritance


I am trying to implement a move/copy assignment operators and constructors in a base class for the derived classes using CRTP.

template <typename Derived>
class base {
public:
    Derived& operator= (const Derived& other) {
        // Copy the base properties:
        this->foo_ = other.foo_;
        // ...
        // Continue as the derived class demands it:
        this->derived().copy(other);
        return this->derived();
    }
    // etc. for copy/move assignment/construction...

private:
    // To act as the derived class:
    Derived& derived () { return *static_cast<Derived*>(this); }
    const Derived& derived () const { return *static_cast<const Derived*>(this); }

protected:
    // Some base properties:
    int foo_;
    // ...
};

class derived: public base<derived> {
    friend base<derived>;

public:
    // Inheriting the constructors and assignment operators:
    using base<derived>::base;
    using base<derived>::operator=;

private:
    void copy (const derived& other) {
        // Copy all the needed derived properties:
        this->bar_ = other.bar_;
        // ...
    }

    // Some derived properties:
    int bar_;
    // ...
};

// To test it:
int main () {
    derived d, t;
    d = t;
}

Compiler gives me an error, saying that derived& derived::operator=(const derived&) cannot be overwritten with derived& base<derived>::operator=(const derived&). My theory is, that somehow derived::operator= gets defined implicitly and then by introducing the base<derived>::operator= by the using declaration I'm trying to redefine it again maybe? This looks suspiciously similar to errors that come up when accidentally defining a method twice.

I compiled this with GCC and the full log is:

test.cpp: In function 'int main()':
test.cpp:25:7: error: 'constexpr derived& derived::operator=(const derived&)' cannot be overloaded
 class derived: public base<derived> {
       ^~~~~~~
test.cpp:4:14: error: with 'Derived& base<Derived>::operator=(const Derived&) [with Derived = derived]'
     Derived& operator= (const Derived& other) {
              ^~~~~~~~

Is this even possible to accomplish, or do I have to define the operators/constructors in the derived class and then delegate their functionality to the base class inside the definition?

Update

OK, maybe after looking at this with a clearer mind, it seems overly complicated. I could just do the following:

Derived& base<Derived>::operator= (const base& other) {
    this->foo_ = other.foo_;
    return this->self();
}

So the returned type is correct for every derived class and the copy is performed from the base class - only the base properties are copied, which is all I need by default. If I need more, then it's specific to each derived class:

// Adding this to the base class - for any derived class to act as the base one:
template <Derived>
base<Derived>& base<Derived>::base () { *return static_cast<base<Derived>*>(this); }

derived& derived::operator= (const derived& other) {
    this->base() = other.base();
    this->bar_ = other.bar_;
}

But still, it's an interesting excercise and the question regarding the compiler error remains unanswered.


Solution

  • You can’t usefully declare a “derived operator=” with the usual signature in a base class because, even with a using-declaration, it is always hidden by the implicitly-declared copy assignment operator. (You could use some other signature for one or both of them, but then overload resolution is likely to be …interesting.)

    Meanwhile, you’ve found a GCC bug in that it incorrectly concludes that the two operators conflict rather than one hiding the other.