Fer*_*eak 4 c++ static templates static-initialization
我最近遇到了一个奇怪的情况.
让我们考虑以下课程(放入header.h):
#ifndef HEADER_H
#define HEADER_H
#include <set>
template <class V, class T>
class Class
{
public:
typedef std::set<const Class<V, T>* > instances_list;
explicit Class(const V& Value):m_value(Value)
{
s_instances.insert(this);
}
private:
static instances_list s_instances;
V m_value;
};
template <typename V, typename T>
typename Class<V,T>::instances_list Class<V,T>::s_instances;
class Something : public Class<int, Something>
{
public:
static const Something SOMETHING_CONSTANT;
private:
explicit Something(int value): Class<int, Something>(value)
{}
};
#endif
Run Code Online (Sandbox Code Playgroud)
以及使用它的非常简单的应用程序:
#include "header.h"
const Something Something::SOMETHING_CONSTANT (1);
int main()
{
}
Run Code Online (Sandbox Code Playgroud)
编译它会产生不同程度的成功.
g ++(4.9.2,4.8.4和4.3.2)编译可执行文件,但它们生成一个SEGFAULT,堆栈跟踪如下:
#0 0x00007ffff7b4aaaa in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#1 0x00000000004012bb in std::_Rb_tree_iterator<Class<int, Something> const*>::operator-- (this=0x7fffffffdcf0) at /usr/include/c++/4.8/bits/stl_tree.h:204
#2 0x0000000000400ef2 in std::_Rb_tree<Class<int, Something> const*, Class<int, Something> const*, std::_Identity<Class<int, Something> const*>, std::less<Class<int, Something> const*>, std::allocator<Class<int, Something> const*> >::_M_get_insert_unique_pos (this=0x6030c0 <Class<int, Something>::s_instances>, __k=@0x7fffffffde08: 0x6030a4 <Something::SOMETHING_CONSTANT>) at /usr/include/c++/4.8/bits/stl_tree.h:1333
#3 0x0000000000400c1d in std::_Rb_tree<Class<int, Something> const*, Class<int, Something> const*, std::_Identity<Class<int, Something> const*>, std::less<Class<int, Something> const*>, std::allocator<Class<int, Something> const*> >::_M_insert_unique (this=0x6030c0 <Class<int, Something>::s_instances>, __v=@0x7fffffffde08: 0x6030a4 <Something::SOMETHING_CONSTANT>) at /usr/include/c++/4.8/bits/stl_tree.h:1377
#4 0x0000000000400b19 in std::set<Class<int, Something> const*, std::less<Class<int, Something> const*>, std::allocator<Class<int, Something> const*> >::insert (this=0x6030c0 <Class<int, Something>::s_instances>,
__x=@0x7fffffffde08: 0x6030a4 <Something::SOMETHING_CONSTANT>) at /usr/include/c++/4.8/bits/stl_set.h:463
#5 0x0000000000400ad9 in Class<int, Something>::Class (this=0x6030a4 <Something::SOMETHING_CONSTANT>, Value=@0x7fffffffde24: 1) at header.h:14
#6 0x0000000000400aa2 in Something::Something (this=0x6030a4 <Something::SOMETHING_CONSTANT>, value=1) at header.h:30
#7 0x0000000000400a24 in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at main.cpp:3
#8 0x0000000000400a6b in _GLOBAL__sub_I__ZN9Something18SOMETHING_CONSTANTE () at main.cpp:7
#9 0x00000000004015ed in __libc_csu_init ()
#10 0x00007ffff751ce55 in __libc_start_main (main=0x4009ed <main()>, argc=1, argv=0x7fffffffdf88, init=0x4015a0 <__libc_csu_init>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdf78) at libc-start.c:246
#11 0x0000000000400929 in _start ()
Run Code Online (Sandbox Code Playgroud)
clang(3.4.1和3.5.0-10)生成一个运行良好的可执行文件,不会出现段错误.
Visual Studio 2015生成一个segfaulting应用程序.
如果我将所有内容放在一个文件中,编译器在ideone.com(http://ideone.com/Dhh8Hl)上发现产生运行时错误,信号11.
我有这种感觉,这是不明确的行为......如果我不对,请纠正我.
阅读相关问题后:C++静态成员初始化(模板内部有趣),模板静态成员初始化顺序和类模板内静态数据的初始化顺序我仍无法从标准中找到相关段落,告诉我为什么在编译时失败g ++和MSVC但传递了clang.
(3.6.2)告诉我:
具有静态存储持续时间(3.7.1)的对象应在任何其他初始化发生之前进行零初始化(8.5).具有静态存储持续时间的引用和具有静态存储持续时间的POD类型的对象可以使用常量表达式初始化(5.19); 这称为常量初始化.零初始化和常量初始化一起称为静态初始化; 所有其他初始化是动态初始化.在进行任何动态初始化之前,应执行静态初始化.对象的动态初始化是有序的或无序的.显式专用类模板的定义静态数据成员已经有序初始化.其他类模板静态数据成员(即,隐式或显式实例化的特化)具有无序初始化.在命名空间作用域中定义的其他对 在单个翻译单元中定义并具有有序初始化的对象应按其在翻译单元中的定义的顺序进行初始化.对于具有无序初始化的对象和在不同转换单元中定义的对象,未指定初始化顺序.
从中我理解Static initialization shall
be performed before any dynamic initialization takes place.并且在我看来const Something Something::SOMETHING_CONSTANT (1);
属于常量初始化的范畴(如果我错了请纠正我)因此它是静态初始化.另外,上面的那个说的Other class template static data
members (i.e., implicitly or explicitly instantiated specializations)
have unordered initialization.很好,因为我只有其中一个,但我不明白为什么静态模板成员在该类型的实际成员之前没有被初始化.
我已经使用https://isocpp.org/wiki/faq/ctors#static-init-order解决了这个问题所以现在我只是好奇为什么编译器有这么不同的行为,这是正确的.
初始化
const Somthing SomeThing::SOMETHING_CONST(1);
Run Code Online (Sandbox Code Playgroud)
是不是常数初始化:它初始化const,但这样做动态的,也就是说,它是动态初始化.计算常量表达式时会发生常量初始化.这是一个更具体的含义const而不仅仅适用于可以在编译期间计算的实体(有关更多详细信息,请参见第5.19节[expr.const]).
如果您希望此初始化作为常量初始化发生,则需要创建构造函数constexpr.鉴于您std::set<int>在初始化期间访问了一个,我怀疑您是否会设法构建您的构造函数constexpr.
这只是使用全局对象的常见危险.如果您需要对初始化顺序进行某种程度的控制,请使用通常的hack来获取至少以适当顺序初始化的全局对象,并将它们包装到返回对本地静态变量的引用的函数中.或者,您可以创建类似于a的constexpr版本的东西std::set<int>,然后可以用于持续初始化.