C ++静态成员初始化(模板内部有趣)

时间:2009-11-30 10:59:10

标签: c++ templates initialization static-members

对于静态成员初始化,我使用嵌套的helper结构,它适用于非模板化的类。 但是,如果封闭类由模板参数化,则如果未在主代码中访问辅助对象,则不会实例化嵌套初始化类。 为了说明,一个简化的例子(在我的例子中,我需要初始化一个向量)。

#include <string>
#include <iostream>

struct A
{
    struct InitHelper
    {
        InitHelper()
        {
            A::mA = "Hello, I'm A.";
        }
    };
    static std::string mA;
    static InitHelper mInit;

    static const std::string& getA(){ return mA; }
};
std::string A::mA;
A::InitHelper A::mInit;


template<class T>
struct B
{
    struct InitHelper
    {
        InitHelper()
        {
            B<T>::mB = "Hello, I'm B."; // [3]
        }
    };
    static std::string mB;
    static InitHelper mInit;

    static const std::string& getB() { return mB; }
    static InitHelper& getHelper(){ return mInit; }
};
template<class T>
std::string B<T>::mB; //[4]
template<class T>
typename B<T>::InitHelper B<T>::mInit;


int main(int argc, char* argv[])
{
    std::cout << "A = " << A::getA() << std::endl;

//    std::cout << "B = " << B<int>::getB() << std::endl; // [1]
//    B<int>::getHelper();    // [2]
}

使用g ++ 4.4.1:

  • [1]和[2]评论说:

    A = Hello, I'm A.

    按预期工作

  • [1]取消注释:

    A = Hello, I'm A.
    B = 

    我希望,InitHelper初始化mB

  • [1]和[2]取消注释:
    A = Hello, I'm A.
    B = Hello, I'm B.
    按预期工作
  • [1]评论说,[2]未注释:
    在[3]
  • 的静态初始化阶段中的Segfault

因此我的问题:这是编译器错误还是坐在显示器和主席之间的错误? 如果是后者:是否有一个优雅的解决方案(即没有显式调用静态初始化方法)?

更新我:
这似乎是一种理想的行为(如ISO / IEC C ++ 2003标准14.7.1中所定义):

  

除非已经显式实例化或明确专门化了类模板或成员模板的成员,否则在需要成员定义存在的上下文中引用特化时,将隐式实例化成员的特化;特别是,除非静态数据成员本身以需要静态数据成员定义存在的方式使用,否则不会发生静态数据成员的初始化(以及任何相关的副作用)。

3 个答案:

答案 0 :(得分:37)

答案 1 :(得分:4)

问题在于,您为静态成员变量提供的定义也是模板。

template<class T>
std::string B<T>::mB;
template<class T>
typename B<T>::InitHelper B<T>::mInit;

在编译期间,这实际上没有定义,因为T是未知的。它类似于类声明或模板定义,编译器在看到它时不会生成代码或保留存储。

当您使用模板类时,定义会在稍后隐式发生。因为在segfaulting案例中你不使用B&lt; int&gt; :: mInit,所以永远不会创建它。

解决方案将明确定义所需成员(不进行初始化):将某个源文件放入

template<>
typename B<int>::InitHelper B<int>::mInit;

这与显式定义模板类的方式基本相同。

答案 2 :(得分:2)

  • [1]未注释案例: 没关系。 static InitHelper B<int>::mInit不存在。如果未使用模板类(struct)的成员,则不会编译它。

  • [1]和[2]未注释案例: 没关系。 B<int>::getHelper()使用static InitHelper B<int>::mInitmInit存在。

  • [1]评论说,[2]没有评论: 它在VS2008中适用于我。