Search code examples
c++templatescrtpstatic-polymorphism

Return reference to this and inheritance


For some syntactic sugar I want to return a reference to this, but when inherited, the function should return the type of the child class:

class base {
  T &operator!() { return *this; }
};
base b; b = !b;

class child : public base {};
child c; c = !c;

Because of the operator, I can't just return the pointer and dynamic_cast it, it has to be a reference.

Is that even possible? Using decltype(*this) for T doesn't work, neither does auto f()->decltype(*this), because of this (although I don't understand why, in the auto-case)

In Scala you can write something like:

template<typename T> class base {
  T &f() { return *this; }
};
class child : public base<child> {};

But my g++ won't accept this (not sure if that's bug or just not in the spec?)

Of course there's the explicit way, but I wonder if this can be avoided using C++11 features?

class child : public base {
  child &operator!() { base::operator!(); return *this }
};

Solution

  • You can use the CRTP to do this if you're allowed to make base a template:

    template <typename Derived> class Base {
    protected:
        Derived& refToThis() {
            return *static_cast<Derived*>(this);
        }
    };
    

    Note the extra cast here. The reason this works is that if you have a class like this one:

    class Subclass: public Base<Subclass> {
        /* ... */
    };
    

    Then if you call refToThis from inside that class, it will call the base class version. Since the class inherits from Base<Subclass>, the instantiated template for refToThis will be

        Subclass& refToThis() {
            return *static_cast<Subclass*>(this);
        }
    

    This code is safe, because the this pointer does indeed point to a Subclass object. Moreover, the static_cast will ensure that the cast fails at compile-time if the derived class doesn't inherit from Base properly, as the pointer type won't be convertible.

    The reason that the cast is necessary here is that if you just say

    template <typename Derived> class Base {
    protected:
        Derived& refToThis() {
            return *this;
        }
    };
    

    Then there is a type error in the program, since a Base by itself is not a Derived, and if you could convert a Base& into a Derived& without any checks you could break the type system.

    That said... I wouldn't do this at all. Overloading operator! for this purpose makes the code less readable, and just writing *this is so idiomatic that hiding it will make your code much harder to understand. Using all of this template machinery to avoid something that's common C++ seems misguided. If you're doing something else before returning the reference that's fine, but this just doesn't seem like a good idea.

    Hope this helps!