非延迟静态成员初始化模板在gcc? [英] non-deferred static member initialization for templates in gcc?

查看:235
本文介绍了非延迟静态成员初始化模板在gcc?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

gcc是否有任何关于静态成员初始化时间的保证,特别是关于模板类?



我想知道我是否可以得到一个硬成员当在多个编译单元中实例化类时,将在 main()之前初始化code> PWrap_T p_s 在main的开头尝试从每个编译单元手动触摸一个符号是不切实际的,但是我不清楚其他什么都可以工作。



我已经用不同单位测试了 bar(),并且总是得到了想要的结果,但我需要知道什么时候/



此外,在库完成加载之前,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 }
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; };

// ***我可以保证这是在main()之前完成的吗? ***
template< class T>
P PWrap_T T :: p_s(T :: id(),T :: desc());

#define PP(ID,DESC,NAME)/ *分号必须跟随! * / \
struct ppdef _ ## NAME {\
constexpr static int id(){return ID; } \
constexpr static char const * desc(){return DESC; } \
}; \
PWrap_T< ppdef _ ## NAME> const NAME

//在除模板/宏标题之外的编译单元中。
void dump(){
std :: cout<< [;
for(P * pp:ps()){std :: cout< < pp-> id_<< :<< pp-> inf_; }
std :: cout<< ]<< std :: endl;
}

//在某些编译单元中。
void bar(int cnt){
for(int i = 0; i PP(2,description,pp)
pp.p_s.doStuff();
}
}

int main(){
dump();
PP(3,another,pp2);
bar(5);
pp2.p_s.doStuff();
}




3.6.2 / 4 - [basic.start.init]:)

这是由实现定义的非局部变量的动态初始化静态存储持续时间在main的第一个语句之前完成。 如果初始化延迟到main的第一个语句之后的某个时间点,它将在与要初始化的变量相同的翻译单元中定义的任何函数或变量的第一个odr使用(3.2)之前发生。



...即使不是odr使用,也必须初始化带有副作用的静态存储持续时间的非本地变量,3.7.1)。

此外,尝试 __ attribute__((init_priority(int))) __ attribute__((constructor))为模板成员的初始化产生警告:括号初始化器之后的属性忽略 ,并且我不知道有关静态初始化的其他技巧。



先感谢任何可以给我一个答案的人!



这里的写法设计为使用共享库。因为共享库可以在main()启动后动态加载,语言规范必须足够灵活以应对它。但是,只要你从翻译单元外部访问你的对象,你就可以保证它在你被访问之前就已经被构建了(除非你在做某些病态的)。



BUT ,如果在同一个编译单元中另一个静态存储持续时间对象的构造函数中使用,则不会阻止它在初始化之前使用。



但是,您可以使用下面的技术轻松地手动提供保证静态对象在使用之前已正确初始化。



只需将静态变量更改为静态函数即可。然后在函数内部声明返回的静态成员。因此,您可以使用与以前完全相同的方式(只需添加())。

 code> template< class T> 
struct PWrap_T
{
static P& p_s(); //将静态变量更改为静态成员函数。
//而不是类型是P使它引用P
//因为对象将被内部保存到函数
};

template< class T>
P& PWrap_T< T> :: p_s()
{
//注意成员是静态的。
//这意味着它将比函数寿命更长。
//也会在第一次使用时初始化。
//并在其他静态存储持续时间对象被销毁时被销毁。
static P p_s_item(T :: id(),T :: desc());

return p_s_item;

//注意它不能保证在main()之前创建。
//但是它保证在第一次使用之前创建。
}

所以这里你可以得到全局的好处



你需要做的唯一修改是:

  void bar(int cnt){
for(int i = 0; i PP(2,description,pp);
pp.p_s()。doStuff();
// ^^在这里添加大括号。
}
}


Does gcc have any guarantees about static member initialization timing, especially regarding template classes?

I want to know if I can get a hard guarantee that static members (PWrap_T<T>::p_s) will be initialized before main(), when classes are instantiated across multiple compilation units. It isn't practical to try to manually touch a symbol from each compilation unit at the start of main, but it isn't clear to me that anything else would work.

I've tested with methods like bar() in different units and always gotten the desired result, but I need to know when/if ever gcc will yank the rug out and whether it's preventable.

Furthermore, will all static members from a DSO be initialized before a library finishes loading?

#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).

Also, trying __attribute__ ((init_priority(int))) or __attribute__ ((constructor)) for the template member's initialization yielded warning: attributes after parenthesized initializer ignored, and I know no other tricks regarding static initialization.

Thanks in advance to anyone who can give me an answer about this!

解决方案

The standard guarantees that static storage duration objects are initialized before any functions/variables in the same translation unit as your object are used from an external source.

The wording here is designed to work with shared libraries. Because shared libraries can be dynamically loaded after main() has started the language specification has to be flexible enough to cope with it. But as long as you access your object from outside the translation unit then you are guaranteed that it will have been constructed before you are given accesses (unless you are doing something pathological).

BUT this does not stop it being used before initialization if it is used in the constructor of another static storage duration object in the same compilation unit.

But you can easily manually provide guarantees that a static object is correctly initialized before used by using the technique below.

Just change the static variable to a static function. Then inside the function declare a static member that is returned. So you can use it exactly the same way as before (just 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.
}

So here you get the benefits of a global (whatever they are). You get guaranteed construction/destruction and you know the object will be correctly constructed before it can be used.

The only change you need to make is:

void bar(int cnt) {
  for (int i = 0; i < cnt; ++i) {
    PP(2, "description", pp);
    pp.p_s().doStuff();
     //   ^^  Add the braces here.
  }
}

这篇关于非延迟静态成员初始化模板在gcc?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆