强制初始化模板类的静态数据成员

时间:2015-01-23 09:25:26

标签: c++ templates optimization g++ one-definition-rule

有一些关于未初始化模板类的静态数据成员的问题。不幸的是,这些都没有能够帮助我解决具体问题的答案。

我有一个模板类,它有一个静态数据成员,必须为特定类型显式实例化(即必须是专用的)。如果不是这种情况,使用不同的模板函数应该会导致链接器错误。

以下是一些代码:

#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>();
}

调用runme<T>应该要求定义Instantiate<T>::instance,而不实际使用它。如图所示获取指向instance的指针有效 - 但前提是未启用任何优化。我需要一个至少与O2一起使用的不同方法,并且如果instance的实例化发生在不同的编译单元中,它也可以工作。

问题: 如果在runme调用T类型时未确定明确Instantiate<T>::instance已定义/专门化,我怎样才能确保收到链接器错误?

1 个答案:

答案 0 :(得分:2)

如果我正确理解您的帖子,您的示例代码可以简化为:

struct X
{
    static int x;
};

int main()
{
    void *f = &X::x;
}

并且您发现仅在未传递-O2的情况下才会生成链接错误。


One Definition Rule非常复杂,但我相信&X::x算作 odr-use 。但是,[basic.def.odr] / 4说:

  

每个程序应该只包含该程序中使用的每个非内联函数或变量的一个定义;无需诊断。

最后3个单词是编译器的一个大黄鼠狼条款,它基本上允许你看到的行为。该程序格式错误(因此生成的任何可执行文件都具有完全未定义的行为)但标准不要求编译器/链接器产生任何警告或错误。

如果ODR规则没有这个转义条款,那么优化器的工作就会困难得多;例如它可能已经确定你的函数只包含死代码,但它必须有额外的逻辑来检查函数中所有 odr-use 的东西。


那么我们该如何解决这个问题呢?由于变量的所有ODR违规都具有相同的&#34;无需诊断&#34;规定,没有保证的解决方案。我们将不得不尝试找到您的特定编译器喜欢的东西,或者阻止优化的方法。

这对我有用gcc 4.8.1:

void *volatile f = &X::x;

(在您的代码示例中也是如此)。这会产生很小的运行时间损失(编译器必须生成对runme的调用的指令)。也许其他人会想出一个更好的技巧:)