我想知道如果我能够在多个编译单元之间实例化类时,在main()之前,静态成员(PWrap_T< T> :: p_s)将被初始化.尝试在主开始手动触摸每个编译单元的符号是不切实际的,但对我来说,其他任何操作都不会很清楚.
我已经用不同单位的bar()方法进行了测试,并且总是得到所需的结果,但是我需要知道什么时候/如果gcc会把地毯弄出来,是否可以防止.
此外,在库完成加载之前,所有来自DSO的静态成员将被初始化吗?
#include <iostream> #include <deque> struct P; inline std::deque<P *> &ps() { static std::deque<P *> d; return d; } void dump(); struct P { P(int id,char const *i) : id_(id),inf_(i) { ps().push_back(this); } void doStuff() { std::cout << id_ << " (" << inf_ << ")" << std::endl; } int const id_; char const *const inf_; }; template <class T> struct PWrap_T { static P p_s; }; // *** Can I guarantee this is done before main()? *** template <class T> P PWrap_T<T>::p_s(T::id(),T::desc()); #define PP(ID,DESC,NAME) /* semicolon must follow! */ \ struct ppdef_##NAME { \ constexpr static int id() { return ID; } \ constexpr static char const *desc() { return DESC; } \ }; \ PWrap_T<ppdef_##NAME> const NAME // In a compilation unit apart from the template/macro header. void dump() { std::cout << "["; for (P *pp : ps()) { std::cout << " " << pp->id_ << ":" << pp->inf_; } std::cout << " ]" << std::endl; } // In some compilation unit. void bar(int cnt) { for (int i = 0; i < cnt; ++i) { PP(2,"description",pp); pp.p_s.doStuff(); } } int main() { dump(); PP(3,"another",pp2); bar(5); pp2.p_s.doStuff(); }
(C++11 §3.6.2/4 – [basic.start.init]:)
It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main,it shall occur before the first odr-use (3.2) of any function or variable defined in the same translation unit as the variable to be initialized.
… A non-local variable with static storage duration having initialization with side-effects must be initialized even if it is not odr-used (3.2,3.7.1).
此外,为模板成员的初始化尝试__attribute__((init_priority(int)))或__attribute__((constructor))产生警告:括号初始化器忽略后的属性,我不知道有关静态初始化的其他技巧.
提前感谢任何人谁可以给我一个答案这个!
解决方法
这里的措辞旨在与共享库共同使用.因为共享库可以在main()启动后被动态加载,所以语言规范必须足够灵活以应对它.但是,只要您从翻译单元外部访问您的对象,那么您将保证在您访问之前已经构建了该对象(除非您正在做某些病态).
但是,如果在同一个编译单元中的另一个静态存储持续时间对象的构造函数中使用,则不会在初始化之前使用它.
但是,您可以轻松地手动提供保证在使用下面的技术之前,静态对象被正确初始化.
只需将静态变量更改为静态函数即可.然后在函数内部声明一个返回的静态成员.所以你可以使用它与以前完全一样(只是add()).
template <class T> struct PWrap_T { static P& p_s(); // change static variable to static member function. // Rather than the type being P make it a reference to P // because the object will be held internally to the function }; template <class T> P& PWrap_T<T>::p_s() { // Notice the member is static. // This means it will live longer than the function. // Also it will be initialized on first use. // and destructed when other static storage duration objects are destroyed. static P p_s_item(T::id(),T::desc()); return p_s_item; // Note its not guaranteed to be created before main(). // But it is guaranteed to be created before first use. }
所以在这里你可以获得一个全球的好处(无论如何).您得到保证施工/破坏,您知道物体将被正确构造,然后才能使用.
您唯一需要做的更改是:
void bar(int cnt) { for (int i = 0; i < cnt; ++i) { PP(2,pp); pp.p_s().doStuff(); // ^^ Add the braces here. } }