问题
Currently I am working on a C++ project in which I plan to embed Lua scripts. For that reason certain classes need to be exported to Lua and I wanted to make this more convenient therefore I created a template class:
template <class T>
class ExportToLua {
public:
ExportToLua() {}
~ExportToLua() {}
private:
static int m_registered;
};
template <class T> int ExportToLua<T>::m_registered = T::exportToLua();
Now every class that needs to be exported is derived from ExportToLua<T>
with T
="the class to be exported". Example:
class Example: public ExportToLua<Example> {
public:
Example();
virtual ~Example();
static int exportToLua();
private:
};
where Example
's static member function exportToLua()
holds the class-specific registration code. My understanding is that an instance of the static member variable ExportToLua<T>::m_registered
exists for every compile unit - that is - for every T
.
But when I start my program the registration code never gets called. For example in example.cpp:
int Example::exportToLua() {
std::cout << "int Example::exportToLua()" << std::endl;
return -2;
}
however I never see this message when I run my program.
Any idea why? Is the compiler some "optimizing away" the static variable m_registered
, because I am not using it anywhere?
Thanks for your input,
Best, Christoph
回答1:
You already found the reason in the standard why the behavior is the way it is. So as a workaround, you can 'trick' the compiler into instantiating that static member by referencing it from either the template constructor or destructor.
#define FORCE_INSTANTIATE(x) (x)
// or (avoids -Wall and -pedantic warnings)
// template <typename T> inline void FORCE_INSTANTIATE(T) {}
template <class T>
class ExportToLua
{
public:
ExportToLua() {}
virtual ~ExportToLua() { FORCE_INSTANTIATE(m_registered); }
private:
static int m_registered;
};
It seems to work in this demo.
Edit: As DyP correctly pointed out, the One-Defintion-Rule comes into play here in whether ExportToLua<T>::m_registered
gets instantiated or not.
To guarantee implicit instantiation, make sure you meet at least one of the following conditions:
- Provide a definition for either the constructor or destructor of the class that's to be exported.
- You create an instance of that class that's used elsewhere in other parts of your code. This will force the compiler to provide a default ctor if you didn't provide one thereby triggering the necessary template instantiations.
If none of those conditions can be met for whatever reason then you'll need to explicitly instantiate the members you want from the template. For example,
class Example: public ExportToLua<Example>
{
public:
// ...
static int exportToLua();
// etc.
};
template int ExportToLua<Example>::m_registered;
You can wrap that into a macro to make it nicer to use if desired.
回答2:
If the compiler implicitly instantiates a class template that contains static members, those static members are not implicitly instantiated. The compiler will instantiate a static member only when the compiler needs the static member's definition.
This behaviour is backed by the C++ standard and here is the passage
14.7.1p1 ... The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, scoped member enumerations, static dataembers and member templates; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions.
and another relevant section found by @gx_
14.7.1p8 The implicit instantiation of a class template does not cause any static data members of that class to be implicitly instantiated.
A work around is the one mentioned by @gx_: Simply add
ExportToLua() { (void)&m_registered; }
to the constructor. Taking the address forces the instantiation of the static variable m_registered.
来源:https://stackoverflow.com/questions/18570632/implicit-initialization-of-static-member-variables-for-template-classes