如何强制依赖静态对象(包括模板成员)的初始化顺序
How to enforce the initialization order of dependant static objects including template members?
我们正在设计一些"类似函数"的数据结构,其中每个对象都是不可变的。为了表示每个容器的空元素,我们决定使用一个静态实例。
我们所做的是在一个文件中定义所有这些静态对象,因此我们可以控制定义的顺序。该头文件包含在主实现文件中。
// 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();
标准
从标准(和这个答案)
类模板的[…]静态数据成员应在隐式实例化它的每个翻译单元[…],除非相应的专门化被显式实例化[…].
由于我们希望避免每个静态对象的显式实例化,因此我们似乎被迫在头文件中保留静态FunctionalArray::gEmpty
的(模板化的)定义(我们可以保证该定义将存在于实例化FunctionalArray
的所有转换单元中的唯一方法)。
问题
现在,我们有一个(非模板化的)静态对象,它的初始化使用模板化静态对象的实例。
// StaticInit.h
#include "FunctionalArray.h"
DependantClass DependantClass::gEmpty = methodReferingToTheEmptyArray();
通过包含FunctionalArray.h(在这里定义了空数组),我们本可以预期不会被静态初始化顺序问题所困扰。。。我们错得再离谱不过了!
问题
- 为什么在我们的案例中没有具体说明订单?(我想这可能是因为编译器仍然只生成空数组的一个实际定义,而这个定义可能在任何其他使用它的编译单元中。但猜测不是很令人满意…)
- 在我们的案例中,是否有一种方法可以指定初始化的顺序,仍然使用"合并定义文件"方法
这里有一个建议,您可能会发现它对解决问题很有用。我使用这种技术通过将静态信息封装在非静态成员中来避免多次实例化。根据标准,在成员函数中定义的静态变量将在首次调用该成员时实例化一次,也请参阅此处的相关问题。由于静态是在调用包含的成员函数后初始化的,因此可以在运行时完全控制它们的初始化。
请注意,这个代码片段中使用的所有类型都在另一个文件中进行了typedef'd,但我相信它们的真实类型是显而易见的。
// 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 %sn", 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;
我希望你觉得这个技巧有用。
相关文章:
- C++成员初始化
- c++构造函数成员初始化:传递参数
- C++正确的指针成员初始化
- 将另一个类的对象传递到当前类C++的构造函数中(不是成员初始化)
- WinLamb 错误:成员初始化非法
- 使用其他成员初始化结构的成员?
- C++模板类静态成员初始化
- 解释了构造函数成员初始化列表
- 如何在成员初始化列表中声明共享指针
- C++入门5版:使用get成员初始化另一个与shared_ptr无关的对象
- C++11 默认类成员初始化与初始值设定项列表同时
- 调用非默认构造函数作为成员初始化
- C++模板成员初始化:用右值移动构造,但用左值移动引用
- 类成员初始化C++
- 在成员初始化列表中,我可以创建对列表中不在列表中的成员变量的引用
- C :(不重复)积分静态成员初始化(不仅是声明!),导致链接器错误,原因
- 如何调用成员初始化器列表中参考成员的构造函数
- C 构造函数采用成员初始化器
- 与其他静态const成员初始化静态常量成员
- 静态内联成员初始化顺序