6

I am trying to implement a template class with a static member. Classes that are derived from the template class shall be instantiated without the need to write extra code.

Here is my naive (and not successful) approach:

Singleton.h:

template <class T> class Singleton {
protected:
  Singleton();
  static T instance_;
}

// explicit instantiation of 'instance_' ???, 
// where 'instance_' is an instance of the derived class
template <class T> T Singleton<T>::instance_;

ConcreteA.h:

class ConcreteA : public Singleton<ConcreteA> {
public:
  ConcreteA();
  void foo();
}

main.c:

int main() {
  // an instance of ConcreteA should have been created (no extra code)!!!
  return 0;
}

Is there a way to force the instantiation of ConcreteA by just deriving ConcreteA from Singleton, without writing extra instantiation code?

A dirty workaround is to call an method on instance_ in the ConcreteA constructor, for example:

ConcreteA.c

ConcrereA::ConcreteA { instance_.foo(); }

Are there better workarounds?

sergej
  • 17,147
  • 6
  • 52
  • 89

4 Answers4

5

Building upon your own "dirty trick", this works in all the compilers I tested, and doesn't require any code in the derived class constructor:

#include <iostream>

template <class T> class Singleton {
protected:
  Singleton() { instptr_ = &instance_; }
  static T instance_;
private:
  static T* instptr_;
};

template<class T> T Singleton<T>::instance_;
template<class T> T* Singleton<T>::instptr_;

class ConcreteA : public Singleton<ConcreteA> {
public:
  ConcreteA() { std::cout << "ConcreteA constructed.\n"; }
  void foo();
};

int main() {
  //Prints 'ConcreteA constructed.'.
  return 0;
}

My understanding of it is that taking the address of instance_ odr-uses it, forcing it into existence. I must say I'm not 100% sure this is guaranteed not to be optimized away in future versions of some compiler (I tested it with -O2 everywhere).

EDIT: Looks like even writing the base class constructor like this

Singleton() { (void)&instance_; }

is enough, which gets rid of instptr_ altogether.

bogdan
  • 9,229
  • 2
  • 33
  • 48
  • 1
    Yes, casting to `void` seems the smallest possible solution here. It is a common way to get rid of unfixable "variable/parameter unused" warnings. – BartoszKP Jan 15 '15 at 15:02
  • @BartoszKP Yeah, that occurred to me immediately after I wrote the first variant, but I decided I'd leave the first one in there as well, as it looks slightly less likely to be optimized away (I still have doubts about this part, with all the 'whole program / link-time optimizations' that compilers use these days). – bogdan Jan 15 '15 at 15:11
  • Is there a way to make the `ConcreteA` constructor `protected`? – sergej Jan 15 '15 at 15:13
  • 1
    @sergej You'd have to add `friend Singleton;` to the definition of `ConcreteA`. That'll do it, since the initialization of `instance_` is in the context of `Singleton`, which now has access to the protected constructor. – bogdan Jan 15 '15 at 15:28
  • 1
    This works in VS 2019. It might be the only answer in the world that works. – cppBeginner Mar 02 '23 at 04:07
2

Unfortunately, that is not possible. Quoting C++11 14.7.1/2 (talking about implicit instantiation of templates):

Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; in particular, the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
0

it's done this way:

template <class T> class Singleton {
protected:
  Singleton();

  // note: static function contains static variable
  static T& instance() {
    static T _t; // c++11 guarantees that this is thread-safe
    return _t;
  }
}

class ConcreteA : public Singleton<ConcreteA>
{
  ...
};

auto& myA = ConcreteA::instance();
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
-2

In C++ static object are guarantee to initialize just if a code from the file that the instance is defined has been called (The order of the initialization is not guarantee).

because of this it better do instance the object inside a function and than call the function

class A{
public:
    static A& getInstance(){
       static A a;
       return a;
    }
protected:
    A(){
    }
};
  • @user2139223 It compiles now, but still: 1) is wrong (usually you don't want to copy a singleton, and your `getInstance` does it). 2) even leaving that aside it doesn't answer the question. – BartoszKP Jan 15 '15 at 15:00
  • I answered the question in the first part, I'm sorry if I didn't been clear enough – user2139223 Jan 18 '15 at 19:23