如何强制执行依赖静态对象(包括模板成员)的初始化顺序?

时间:2013-11-27 10:57:23

标签: c++ templates static-members

我们正在设计一些“类似功能”的数据结构,其中每个对象都是不可变的。为了表示每个容器的空元素,我们决定使用静态实例。

我们正在做的是在单个文件 StaticInit.h 中定义所有这些静态对象,因此我们可以控制定义的顺序。该头文件包含在主实现文件中。

// StaticInit.h
FunctionalMap FunctionalMap::gEmpty = FunctionalMap();

模板化类和静态对象

当我们实现一些模板化结构时会出现并发症:

// FunctionalArray.h
template <class T_contained>
class FunctionalArray
{
    static FunctionalArray gEmpty;

    private:
        FunctionalArray();
}

template <class T_contained>
FunctionalArray<T_contained> FunctionalArray<T_contained>::gEmpty = FunctionalArray();

标准

来自标准(和this answer

  

类模板的静态数据成员应在中定义   隐含实例化的每个翻译单元[...],   除非显式实例化相应的特化   [...]。

由于我们希望避免每个静态对象的显式实例化,似乎我们不得不在头文件中保留静态FunctionalArray::gEmpty的(模板化)定义(我们可以保证定义的唯一方法)存在于实例化FunctionalArray)的所有翻译单元中。

问题

现在,我们有一个(非模板化的)静态对象,初始化使用模板化静态对象的实例。

// StaticInit.h
#include "FunctionalArray.h"
DependantClass DependantClass::gEmpty = methodReferingToTheEmptyArray();

通过包含FunctionalArray.h(完成空数组的定义),我们原本不希望被静态初始化顺序问题所困扰......我们不能错了!

问题

  • 为什么在我们的案例中没有指明订单? (我猜这可能是因为编译器仍然只生成空数组的一个实际定义,并且这个定义可以在使用它的任何其他编译单元中。但猜测不是很令人满意.. 。)
  • 有没有办法在我们的案例中指定初始化顺序,仍然使用“合并定义文件”方法?

1 个答案:

答案 0 :(得分:0)

以下是您可能会发现有助于解决问题的提案。我使用这种技术通过将静态信息封装在非静态成员中来避免多个实例化。根据标准,成员函数中定义的静态变量将在首次调用成员时实例化,也可以参见相关问题here。由于在调用包含成员函数之后初始化静态,因此可以在运行时完全控制它们的初始化。

请注意,此代码段中使用的所有类型都是在另一个文件中键入的,但我相信它们的真实类型很明显。

// type to name mapper template
template<typename T>
struct TP
{
    struct info
    {
        info() : pT(0), sz(0) { }
        Char *pT;
        UInt sz;
        Char *name() { return pT; }
        UInt size() { return sz; }
    } _info;

    TP() { }
    TP(const Char *pName) { typeInfo(pName); }

    info &typeInfo(const Char *pN=0) 
    { 
        static TP<T> x; 
        if (x._info.pT==0) { x._info.pT = (Char *)pN; x._info.sz=sizeof(T);/* prin tf("==New Type %s\n", pN);*/}
//      Assert(x._info.pT !=0 || "TP::typeInfo encountered an unknown type" == 0);
        return x._info; 
    }
};

struct InitBarrayTypes
{
    InitBarrayTypes()
    {
        TP<Char> ctp("char");
        TP<Int> itp("int");
        TP<Long> ltp("long");
        TP<Double> ftp("float");
    }
};

// this will be created many times, but has no data and so takes no space,
// and finally only the static members of TP within typeInfo() will remain
static InitBarrayTypes s__ibt;

以上使用如下:

cout << "Name of Int is " << TP<Int>().typeInfo().name() << endl;

我希望你发现这项技术很有用。