问题
There have been a few questions about static data members of template classes not being initialized. Unfortunately none of these had answers that were able to help me with my specific problem.
I have a template class that has a static data member that must be instantiated explicitly for specific types (i.e., must be specialized). If this is not the case, using a different template function should cause a linker error.
Here's some code:
#include <iostream>
template <typename T>
class Instantiate {
public:
static Instantiate instance;
private:
Instantiate(std::string const &id) {
std::cout << "Instantiated " << id << "." << std::endl;
// Additional, important side effects...
}
};
template <typename T>
void runme() {
// Do something to ensure instance is instantiated,
// without creating run-time overhead.
// The following only works without optimization.
void *force = &Instantiate<T>::instance;
}
// Instances need to be explicitly specialized for specific types.
template <> Instantiate<int> Instantiate<int>::instance = {"int"};
int main() {
// This is OK, since Instantiate<int>::instance was defined.
runme<int>();
// This should cause a (linker) error, since
// Instantiate<double>::instance is not defined.
runme<double>();
}
Calling runme<T>
should require that Instantiate<T>::instance
is defined, without actually using it. Getting a pointer to instance
as shown works - but only if no optimizations are enabled. I need a different method that works with at least O2
, and also works if the instantiation of instance
occurs in a different compilation unit.
Question:
How can I ensure that I get a linker error when calling runme
with a type T
for which no explicit Instantiate<T>::instance
has been defined/specialized?
回答1:
If I understand your post correctly, your sample code can be reduced to:
struct X
{
static int x;
};
int main()
{
void *f = &X::x;
}
and you are finding that a link error is only generated if -O2
is not passed.
The One Definition Rule is extremely complicated but I'm fairly confident that &X::x
counts as odr-use. However , [basic.def.odr]/4 says:
Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.
The last 3 words is a big weasel clause for compilers, it basically permits the behaviour that you are seeing. The program is ill-formed (and so any executable generated has completely undefined behaviour) but the standard does not require that the compiler/linker produce any warnings or errors.
If the ODR rule didn't have this escape clause then the optimizer's job would be much more difficult; e.g. it could have identified that your function only contains dead code but it would have to have extra logic to check for all odr-use of things in the function.
So how do we fix this? Since all ODR violations for variables have the same "no diagnostic required" provision, there's no guaranteed solution. We will have to try and find something that your particular compiler likes, or a way to prevent optimization.
This worked for me with gcc 4.8.1:
void *volatile f = &X::x;
(and the same thing worked in your code sample). This will incur a small runtime penalty though (the compiler has to generate an instruction for the call to runme
). Maybe someone else will come up with a nicer trick :)
来源:https://stackoverflow.com/questions/28106661/forcing-initialization-of-static-data-member-of-template-class