1

I am trying to implement the curiously recurring template pattern (CRTP) to access a member variable of a child class from the parent class, but I am getting a compilation error saying I am illegally referencing a non-static member variable.

#include <iostream>

template <typename Child>
class Parent
{
public:
    int get_value()
    {
        return Child::m_value;
    }

    virtual ~Parent() = default;
};

class Child : public Parent<Child>
{
    int m_value = 42;

    friend class Parent<Child>;
};

int main()
{
    Child child;
    std::cout << child.get_value() << std::endl;
}

Error:

illegal reference to non-static member 'Child::m_value'

How can I properly access the member variable of the child class from within the parent class?

Is CRTP even the best/cleanest approach here?

tjwrona1992
  • 8,614
  • 8
  • 35
  • 98
  • The compiler is literally saying that it can't access because the member is non-static... so first you should make it static? – user202729 Feb 18 '21 at 03:53
  • It needs to be non-static. I will have many instances of the child class that will have different values for this variable. (It won't always be 42) – tjwrona1992 Feb 18 '21 at 03:54
  • 2
    Huh, I see, then you need `static_cast(*this)` (*looking for a duplicate, if any*) – user202729 Feb 18 '21 at 03:55
  • There's https://stackoverflow.com/questions/48945671/curiously-recurring-template-pattern-illegal-call-of-non-static-member-funciton which is close but not the same. Besides that answer doesn't mention the `T& underlying(){ return ... }` which I think most people uses. – user202729 Feb 18 '21 at 03:57
  • Try `return dynamic_cast(this)->m_value;` instead. – dxiv Feb 18 '21 at 03:59
  • That having said, surely some source that explains how to use CRTP must also explain how to access the `*this` object? – user202729 Feb 18 '21 at 04:00
  • 2
    @dxiv Dynamic_cast is unnecessarily inefficient in this case. – user202729 Feb 18 '21 at 04:00
  • @user202729 I'm starting to think `T& underlying(){ return ... }` is going to be a more elegant solution. Ultimately my child class will also have children of its own and the CRTP doesn't seem to work well down multiple levels of inheritance. – tjwrona1992 Feb 18 '21 at 04:02
  • @user202729 It's still the safest way to check that all works as expected. Once satisfied, the OP can change that to a `static_cast` if they want. Either way, point remains that `Parent` doesn't know in advance that it will be used as a base class in CRTP, so it can't resolve `Child::m_value`. – dxiv Feb 18 '21 at 04:02
  • @dxiv No. A static cast is not only largely sufficient, but also totally safe. The relation between Parent and Child is very well known at compile time. Also , the point of using CRTP is to avoid using virtual functions and RTTI. Reserve usage of dynamic_cast<> for casting from a type that can ony be known at runtime, that's what dynamic_cast<> is for. – Michaël Roy Feb 18 '21 at 07:07
  • @MichaëlRoy That's assuming `Parent` is always and only used in the correct CRTP pattern. Otherwise `static_cast` offers no protection against misuse, while `dynamic_cast` does. Compare the two outputs [here](https://godbolt.org/z/br8Yh1) for example, where the first line could technically result in a corruption or crash instead of just the wrong output. Whether such misuse is a concern or not depends on OP's context and use-case. – dxiv Feb 18 '21 at 07:33
  • @dxiv Yes. Of course. That's assuming that `template Parent{};` has a derived class aliased by the class-scoped template argument named Child. If that is not the case, `static_cast(this)` will not compile. This means `static_cast(this)` **enforces** correct CRTP usage, while the use of `dynamic_cast(this)` does not, since it could compile even if were not possible at runtime. – Michaël Roy Feb 18 '21 at 08:23
  • @dxiv See my addition to the answer below. – Michaël Roy Feb 18 '21 at 08:41
  • @MichaëlRoy My only nitpick is that claiming `static_cast` is completely safe in all conceivable cases is too strong. The snippet linked in my previous comment gives an explicit counterexample to that claim. Other than that, yours is a good answer, which I had +1'd already. – dxiv Feb 18 '21 at 09:12

1 Answers1

3

Here is the correct way to access members of a CRTP derived class.

template <typename Child>
class Parent
{
  public:
    int get_value()
    {
        // Do NOT use dynamic_cast<> here.
        return static_cast<Child*>(this)->m_value;
    }

    ~Parent() { /*...*/ }; // Note: a virtual destructor is not necessary,
                           // in any case, this is not the place to
                           // define it.
};

// A virtual destructor is not needed, unless you are planning to derive 
// from ConcreteClass.

class ConcreteClass : public Parent<ConcreteClass> 
{
    friend class Parent<ConcreteClass>;  // Needed if Parent needs access to 
                                         // private members of ConcreteClass

    // If you plan to derive from ConcreteClass, this is where you need to declare
    // the destructor as virtual.  There is no ambiguity as to the base of
    // ConcreteClass, so the static destructor of Parent<ConcreteClass> will
    // always be called by the compiler when destoying a ConcreteClass object. 
    //
    // Again: a virtual destructor at this stage is optional, and depends on 
    // your future plans for ConcreteClass.
  public:
    virtual ~ConcreteClass() {};

  private:
    int m_value;
}; 

// only ConcreteClass needs (optionally) a virtual destructor, and
// that's because your application will deal with ConcretClass objects
// and pointers, for example, the class below is totally unrelated to 
// ConcreteClass, and no type-safe casting between the two is possible.

class SomeOtherClass : Parent<SomeOtherClass> { /* ... */ }

ConcreteClass obj1;
// The assignment below is no good, and leads to UB.
SomeOtherClass* p = reinterpret_cast<ConcreteClass*>(&obj1); 

// This is also not possible, because the static_cast from
// Parent<UnrelatedClass>* to UnrelatedClass* will not compile.
// So, to keep your sanity, your application should never  
// declare pointers to Parent<T>, hence there is never any 
// need for a virtual destructor in Parent<> 

class UnrelatedClass {/* ... */ };

auto obj2 = Parent<UnrelatedClass>{};

As the concrete type ConcreteClass and its relation to Parent is known ate compile-time, a static_cast is sufficient to convert this from Parent<ConcreteClass>* to a ConcreteClass*. This provides the same functionality as virtual functions without the overhead of a virtual function table, and indirect function calls.

[edit]

Just to be clear:

template <typename Child>
class Parent
{
  public:
    int get_value()
    {
        // the static cast below can compile if and only if
        // Child and Parent<Child> are related.  In the current 
        // scope, that's possible if and only if Parent<Child>
        // is a base of Child, aka that the class aliased by Child
        // was declared as:
        //   class X : public Parent<X> {};
        //   
        // Note that it is important that the relation is declared 
        // as public, or static_cast<Child*>(this) will not compile.
        //
        // The static_cast<> will work correctly, even in the case of 
        // multiple inheritance. example:
        //
        //   class A {];
        //   class B {};
        //   class C : public A
        //           , public Parent<C> 
        //           , B  
        // {
        //     friend  class Parent<C>;
        //     int m_value;
        // }; 
        //
        // Will compile and run just fine.

        return static_cast<Child*>(this)->m_value;
    }
};

[edit]

If your class hierarchy gets a bit more complex, the dispatching of functions will look like this:

template <typename T>
class A
{
public:
  int get_value()
  {
      return static_cast<T*>(this)->get_value_impl(); 
  }

  int get_area()
  {
      return static_cast<T*>(this)->get_area_impl(); 
  }
};

template <typename T>
class B : public A<T>
{
    friend A<T>;
protected:
    int get_value_impl()
    {
        return value_;
    }
    
    int get_area_impl()
    {
        return value_ * value_;
    }

private:
   int value_; 
};

template <typename T>
class C : public B<T>
{
    // you must declare all bases in the hierarchy as friends.
    friend A<T>;
    friend B<T>;
protected:
    // here, a call to C<T>::get_value_impl()
    // will effetively call B<T>::get_value_impl(), 
    // as per usual rules.
  
    // if you need to call functions from B, use the usual 
    // syntax 
    
    int get_area_impl()
    {
        return 2 * B<T>::get_value_impl();
    }
};
Michaël Roy
  • 6,338
  • 1
  • 15
  • 19
  • "*that's possible if and only if `Parent` is a base of `Child`*" Correct. "*aka that the class aliased by `Child` was declared as `class X : public Parent`*" But this does not follow, unless you assume that `Parent` is *only* used as the base for `Child` or another `Child`-derived class. However, if by mistake or mischief you *add* a `class Bastard : public Parent` the code will happily compile, yet calling `Bastard().get_value()` will result in undefined behavior. – dxiv Feb 18 '21 at 09:13
  • @dxiv The c++ standard does not define any rule that prevents programmers from going completely crazy and sabotaging their own work either; – Michaël Roy Feb 18 '21 at 10:12
  • Accepting this answer because I feel this is the correct solution for the question asked, although after looking further into my particular problem I think the best option for me is to use a protected function `int& get_value() { return m_value; }` that each derived class will implement. This method will work better for my particular problem because my child classes will have children of their own and trying to make CRTP work more than one level deep becomes very ugly very fast. – tjwrona1992 Feb 18 '21 at 23:02
  • @tjwrona1992 You can 'nest' CTRP to several layers,deep, the selection of functions and members will work much like virtual function mechanism. – Michaël Roy Feb 18 '21 at 23:15
  • Yes but then doesn't it start to look really ugly? At least the only way I was able to get it to work looked god awful. You end up with things like `template class Child : public Parent> { ... }` Am I missing something? Or is there a cleaner nicer way to do this? – tjwrona1992 Feb 18 '21 at 23:19
  • @tjwrona1992 It is not that bad, see add-on to answer. And it also works rather well for multiple inheritance as well. – Michaël Roy Feb 18 '21 at 23:43
  • Interesting, I'll play around with that a bit and see what I can do with it. Thanks for the help :) – tjwrona1992 Feb 18 '21 at 23:59