2013-08-27 2 views
4

Я использую что-то вроде следующих разделов кода для инициализации. Я знаю, что инициализация p<T>::i_ неупорядочена. Я считаю, что h заказано, поэтому я должен иметь возможность обосновать порядок, в котором он инициализирован. Учитывая, что заголовок для p включен до определения h, есть ли гарантия, что p<T>::i_ будет инициализирован до h?Гарантирован порядок инициализации

struct helper 
{ 
    template <typename T> 
    helper(const T&, int i) 
    { 
     p<T>::i_::push_back(i); 
    } 
}; 
static helper h; 

Класс p определен ниже.

template <typename T> 
struct p 
{ 
    static std::vector<int> i_; 
}; 
template <typename T> 
std::vector<int> p<T>::i_; 
+0

Это не скомпилируется - 'helper' не имеет конструктора по умолчанию. –

+0

Какой конструктор используется для инициализации 'h'? –

+3

«Я считаю, что« h »заказано». Упорядочено по сравнению с чем? – jrok

ответ

6

Порядок инициализации объектов со статической продолжительностью хранения не определен в единицах перевода и последователен в каждой единицы перевода.

В вашем конкретном случае все сложнее, поскольку один из объектов со статическим хранилищем является статическим членом класса шаблона. Практически это означает, что каждая единица перевода, которая обращается к члену p<T>::i_, создаст этот символ и добавит соответствующий код инициализации. Позже компоновщик выберет один из экземпляров и сохранит его. Даже если он выглядит так: p<T>::i_ определен доh в вашей единицы перевода, вы не знаете, какой экземпляр p<T>::i_ будет храниться в линкере, и это может быть один в другой единицы перевода, и, следовательно, заказ не гарантируется ,

В общем, это плохая идея иметь глобальные объекты, я бы предложил вам перепроектировать вашу программу без этих глобальных переменных.

+0

+1 Для обозначения единиц перевода/участия линкера. Мне было интересно, инициализируется ли 'static 'не шаблонная переменная в шаблоне при создании экземпляра шаблона? Или он инициализируется независимо от того, был ли шаблон создан или нет (кажется немного странным)? – lapk

+0

Важно то, что неявное создание шаблона * не * инициирует инициализацию статических членов. Они инициализируются, когда они используются таким образом, который требует определения. – jrok

+0

@PetrBudnik: Если вы явно не создаете экземпляр шаблона, все члены создаются по запросу. Если программа не использует статический член шаблона, член не создается компилятором, если только он не используется. –

5

Объекты в глобальном масштабе или пространстве пространства имен строятся сверху вниз в пределах одной единицы перевода. Порядок построения глобального уровня или пространства имен между разными единицами перевода не определен. Самый разумный способ заказать инициализацию между единицами перевода его завернуть объекты в пределах соответствующих функций доступа, например:

template <typename T> 
something<T>& get() { 
    static something<T> values; 
    return value; 
} 

Заметим, однако, что это не поточно- в C++ 03 (как C++ 03 не имеет понятия нитей в любом случае). Он является потокобезопасным в C++ 11.

0

Нет, это не гарантируется.

Что вы можете сделать, однако:

template<typename T> 
std::vector<int>& registry() { 
    static std::vector<int> reg; 
    return reg; 
} 

... 
registry<T>().push_back(i); 
... 

Еще лучше избегать делать вещи, которые являются слишком умны, во время запуска.

Отладка до начала или после окончания main - это настоящий кошмар (и IMO даже не покрыт 100% стандартом). Простая регистрация может быть в порядке, но никогда не делайте ничего, что может потерпеть неудачу.

На протяжении многих лет я отходил от этого подхода к явной инициализации/выключению и никогда не оглядывался назад.

+0

Отладка динамической инициализации может быть не такой сложной, как вы думаете. Существует зависящий от платформы символ, который вы можете сломать, который отмечает запись для динамической инициализации, а затем вы можете пройти через нее точно так же, как любая функция. –

+0

@ user1131467: Я видел, что инструменты отладки не работают должным образом во время инициализации и во время выключения. Обратите внимание, что во время инициализации даже не ясно, какая часть стандартной библиотеки уже инициализирована и может быть использована, и какая часть стандартной библиотеки уже отключена во время уничтожения экземпляра статической продолжительности. Добавьте к этому, что, например, окна в некоторых случаях просто молчаливо проглатывают segfault при выходе из приложения ... – 6502

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