2015-11-25 1 views
4

Недавно я столкнулся с необычной ситуацией.Повторный классический статический запрос инициализации C++

Давайте рассмотрим следующий класс (место в 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 

и очень простое приложение, используя его:

#include "header.h" 

const Something Something::SOMETHING_CONSTANT (1); 

int main() 
{ 
} 

Компиляция это приводит к различным степеням успешностью.

г ++ (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>, [email protected]: 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>, [email protected]: 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>, 
    [email protected]: 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>, [email protected]: 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() 

звоном (3.4.1 и 3.5.0 -10) создать исполняемый файл, который работает хорошо, не segfault.

Visual Studio 2015 создает приложение для segfaulting.

Если я ставлю все в одном файле компилятор нашел в ideone.com (http://ideone.com/Dhh8Hl) выдает ошибку во время выполнения, сигнал 11.

У меня есть ощущение, что это неопределенное поведение ... Пожалуйста, поправьте меня, если я Я не прав.

После прочтения соответствующих вопросов: C++ Static member initalization (template fun inside), Template static members initialization order и Initialization order of static data inside class template Я до сих пор не удалось найти соответствующие пункты из стандарта, которые говорят мне, почему это терпеть неудачу при компиляции с г ++ и MSVC, но проходит по звоном.

The (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, так что теперь мне просто интересно, почему существует такое другое поведение от компиляторов, и что правильно.

+0

Это не POD (у него есть конструктор), и, следовательно, это не статическая инициализация. –

ответ

5

Инициализация

const Somthing SomeThing::SOMETHING_CONST(1); 

является не постоянной инициализации: она инициализирует const, но делает это динамически, то есть он является динамической инициализации. Постоянная инициализация происходит при вычислении постоянного выражения . Это более конкретный смысл, чем просто const и применяется только к объектам, которые могут быть вычислены во время компиляции (подробнее см. Раздел 5.19 [expr.const]).

Если вы хотите, чтобы эта инициализация выполнялась как постоянная инициализация, вам нужно сделать свой конструктор constexpr. Учитывая, что во время этой инициализации вы получаете доступ к std::set<int>, я сомневаюсь, что вам удастся сделать ваш конструктор constexpr.

Это обычная опасность использования глобальных объектов. Если вам нужен некоторый уровень управления порядком инициализации, используйте обычный хак, чтобы получить глобальные объекты, по крайней мере, инициализированные в подходящем порядке и завернуть их в функцию, возвращающую ссылку на локальную статическую переменную. В качестве альтернативы вы можете создать constexpr версию std::set<int>, которая затем может быть использована для постоянной инициализации.

+0

Хорошая уловка для неконстантной, но динамической инициализации! К сожалению, у меня нет доступа к C++ 11, поэтому мне придется жить с этим вариантом. – fritzone

+0

Dietmar, правильно ли обобщить, что в любое время, когда нелокальный статический объект (будь то пространство имен, область видимости или внешний объект), становится доступным статически, мне нужно снова защищать SIOF? Подумайте, например, о функции 'f()', определенной в '1.cpp', которая вызывается для инициализации статического объекта' var' в '2.cpp', и предположим, что' f() 'возвращает ** non- extern ** const 'k' (т.е.' k' определяется в '1.cpp' так же, как' f() '). – Kemal

+0

Дополнение к моему сообщению выше: пусть 'const int k = 99', так что мы находимся в ясности в том смысле, что' k' сам не склонен к SIOF. – Kemal

Смежные вопросы