Forcing initialization of static data member of template class

人盡茶涼 提交于 2019-12-11 03:42:34

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!