Reputation: 7623
I've been doing some research into this problem but haven't yet been able to come up with a solution. Basically I need to initialize a static const type variable inside a template class.
class MyType
{
public:
MyType (int a, int b) { }
};
template <class T>
class MyClass
{
public:
static const MyType Type;
};
Initializing Type inside the cpp will produce a linker error. Initializing Type inside the header will cause it to be initialized multiple times. Initializing Type inside the class cannot be done due it being a non-integral type. How can I solve this problem without limiting class specialization. Any help is appreciated.
Upvotes: 4
Views: 3662
Reputation: 131907
Initializing Type inside the header will cause it to be initialized multiple times.
Well, of course. One time for every different type that MyClass
is instantiated with. It's also a different object for every type, that's inherent to how templates work. If you only want it defined and initialized once, put it in a non-template base:
namespace detail{
class MyClassBase{
protected:
~MyClassBase(){} // only usable as a base class, non-polymorphic
static const MyType Type; // only available to derived types
};
} // detail::
template<class T>
class MyClass
: private detail::MyClassBase // private, non-polymorphic
{
public:
using MyClassBase::Type; // if you want to expose 'Type' to the public audience
};
Now you can just put
const MyType detail::MyClassBase::Type = /*initialize here*/;
in a .cpp and be done with it.
Note that it would generally be better to encapsulate the static
object inside a function, as @Dietmar shows. Those function-local statics are superior to any other kind of static object, because you won't run into the static initialization order fiasco when using them.
Upvotes: 1
Reputation: 154035
I'm not sure what you mean by "initializing Type inside the cpp will produce a linker error." but assuming you actually mean defining then you must have done something wrong because defining the static member for every type in an appropriate location certainly works! What you have in you class template is a declaration of an object and this needs to be defined somewhere if it ever referenced. Only if MyType
happens to be an integral type, you initialize it in your class [template], and you never need its address (e.g. bind it to a constant reference or take its address) you get away with not defining it. This is because it is always treated as a constant expression in this case.
My guess is that you tried to define your object something like this in some cpp file:
template <typename T> MyType const MyClass<T>::Type = some-initialization-here;
This won't work unless you also instantiate this definition either explicitly or implicitly in the same translation unit. You can define the member for a specific type something like this:
template <> MyType const MyClass<T>::Type = some-initialization-here;
Unless you actually need the type to be a constant expression in which case you can typically side-step the problem, if necessary by making it an enum
(this is what I tend to do because this guy can be bound to const
reference without requiring a definition), you can use a static member function which can be defined in the header instead:
template <typename T>
MyType const& MyClass<T>::Type() {
static MyType rc = some-initialization-here;
return rc;
}
BTW, I'm pretty sure this question was answered before, definitely in comp.lang.c++.moderated.
Upvotes: 3