0

I try to initialize a static member of a CRTP base class. The base class holds a static template member of the derived class, while the derived class defines other data members. I want to statically initialize this variable. In code, a minimal example would be the following:

template<typename DERIVED>
class base {
    public:
        static DERIVED x;   
};

class derived : public base<derived>
{
    public:
        int a;   
};

int base<derived>::x {0};

int main()
{}

However, the above does not compile, but gives the error message error: specializing member 'base<derived>::x' requires 'template<>' syntax. How can I get the static initialization to work?

2 Answers2

0

The correct syntax is:

template <class T>
class base {
  public:
    static T x;   
};

template <class T>
T base<T>::x{};

Note that you can't initialize x to a specific integer value, since then your base class won't compile when DERIVED is e.g. an std::string.

You probably also want your inheritance to be explicitly public or private. I.e.:

class derived : public base<derived> {
  public:
    int a;   
};

Edit: To specialize x and it's initialization for specific types T, you have to specialize base itself. I.e.:

class A;

template <>
class base<A> {
  public:
    static int x;   
};

// Note missing "template <>" here.
int base<A>::x{ 5 };

class A : public base<A> { };

This seems rather cumbersome, since you need to already forward declare A in this case before declaring the specialization. To me it feels you might want to rethink your design. Is this really required or is there a simpler way to accomplish what you want?

AVH
  • 11,349
  • 4
  • 34
  • 43
  • Thank you for the answer. Yes, I forgot to indicate public inheritance. I was aware that I can initialize as you suggested. However, I would specifically like to initialize the case when I use the above given derived class and only for this case. Other derivations from base could handle initialization differently. – Paul Swoboda Oct 29 '20 at 09:57
  • @PaulSwoboda In that case you'll have to specialize the base class. See my updated answer. – AVH Oct 29 '20 at 10:04
  • This seems like it would defeat the purpose of the setup, since I would be unable to share a base class for different derived ones. – Paul Swoboda Oct 29 '20 at 10:46
  • @PaulSwoboda That's simply an effect of using CRTP. Your base class is templated, so it will always be a different type for each different derived class. Depending on your actual use case, you can make `base` derive from a non-templated base class. – AVH Oct 29 '20 at 10:57
  • Also, if this answers your question, please upvote/accept. – AVH Oct 29 '20 at 10:57
0

I have found the static initializer trick, which works in the above setting, too. It was described here and here. In the above context, it would be as follows:

#include <iostream>

template<typename DERIVED>
class base {
    public:
        static inline DERIVED x;   
};

class derived : public base<derived>
{
    public:
        int a;
        
        struct static_initializer {
            static_initializer()
            {
                derived::x.a = 1;
            }
        };
         
        static inline static_initializer static_initializer_;        
};

int main()
{
    std::cout << derived::x.a << "\n";
}

The above output the value 1.

Note: this works with C++17.