Forcing the instantiation of static members in a template class

184 views Asked by At

I want to use a static member in a template class to instantiate a singleton object for each class that inherits from that template class.

Here is an example:

#include <iostream>
#include <vector>

struct X{

    static std::vector<X*>& getRegistry(){
         static std::vector<X*> registry;
         return registry;
    }

    X(){
        getRegistry().push_back(this); // Each X adds itself to the registry
    }
};

template<typename T>
struct Y : X{ 
private:   
   static T instance; // The per-type singleton
};

template<typename T>
T Y<T>::instance {};

The idea is that objects of type X enter themselves into a "registry" whenever they are created. Class Y<T> that is derived from X is a template class that should instantiate a static singleton object of each type that inherits from it (using the curiously recurring template pattern).

The idea is that the "registry" will contain one object of each class that inherits from Y<T>. These classes should not have to do anything to get added to the registry; instead, simply inheriting from Y<T> should be enough to create a singleton object that is added to the registry.

I tried my code like this:

struct A : Y<A>{};
struct B : Y<B>{};
struct C : Y<C>{};

int main(){
    std::cout << "Number of objects in the registry: " << X::getRegistry().size() << std::endl;
    // Should print "3"
}

Fiddle link: http://ideone.com/aWDEg4

The desired behaviour should be that one A, one B, and one C, should be in the registry. But none of them is. The problem is that Y<T>::instance is not instantiated for any of the classes as it is not used in the code. However, I never want to use these fields, they are just their to instantiate the singleton. So is there a way to force the instantiation of the static field without having to add additional code to the deriving classes (i.e., A,B, and C)?

I know I can explicitly instantiate template class Y<A>;, but this would add additional code to the deriving class A.

2

There are 2 answers

1
sjdowling On

You could just create static instances:

struct A : Y<A>{};
struct B : Y<B>{};
struct C : Y<C>{};

static A a;
static B b;
static C c;

int main(){
    std::cout << "Number of objects in the registry: " << X::getRegistry().size() << std::endl; 
}

output:

Number of objects in the registry: 3
0
b4hand On

I don't think you can avoid involving the clients of Y, but you can make Y's constructor private forcing client involvement. For example, this works as you want:

template<typename T>
struct Y : X {
   Y(const Y<T> &other) {}
   static Y<T> create() { &instance; return Y<T>(); }
private:
   static T instance; // The per-type singleton
   Y() {}
};

template<typename T>
T Y<T>::instance {};

struct A : Y<A> { A() : Y(Y::create()) {} };
struct B : Y<B> { B() : Y(Y::create()) {} };
struct C : Y<C> { C() : Y(Y::create()) {} };

The client involvement is the invocation of Y::create which also happens to force the instantiation of the instance member. Granted, this is probably not what you wanted, but it does work. Note also, my previous comment about the this pointer will still apply even if you can add the static instances to your registry.