Стационарные члены класса гарантированно инициализируются до вызова `main`? - программирование
Подтвердить что ты не робот

Стационарные члены класса гарантированно инициализируются до вызова `main`?

Есть ли какая-либо гарантия, что статические члены класса инициализируются до вызова main?

4b9b3361

Ответ 1

Я думаю, нет:

[C++03: 3.6.2/3]: Определяется реализацией независимо от того, выполняется ли динамическая инициализация (8.5, 9.4, 12.1, 12.6.1) объекта области пространства имен до первого оператора main. Если инициализация отложена до некоторого момента времени после первого утверждения main, она должна произойти до первого использования любой функции или объекта, определенного в той же самой единицы перевода, что и объект инициализироваться.


Хм, правда?

Ну, возможно, "определенный в пространстве имен" не совсем то же самое, что "объект области пространства имен":

[C++03: 9.4.2/2]: Объявление члена данных static в определении его класса не является определением и может быть неполным, отличным от cv-qual void. Определение для члена данных static должно отображаться в области пространства имен, содержащей определение класса участника. В определении в области пространства имен имя члена данных static должно быть присвоено его именем класса с помощью оператора ::. Инициализатор выражение в определении статического члена данных входит в сферу его класса (3.3.6).

Однако, это инициализатор, который в области класса; нет упоминания о самом элементе static, имеющем что-либо иное, кроме области пространства имен (если мы мысленно не вводим слово "лексически" всюду).

Вот этот приятный абзац:

[C++03: 9.4.2/7]: Элементы статических данных инициализируются и уничтожаются точно так же, как нелокальные объекты (3.6.2, 3.6.3).

Однако, к сожалению, единственным дополнительным определением последовательности main и статической инициализации по отношению к "нелокальным объектам" является вышеупомянутое [C++03: 3.6.2/3].


Итак, что тогда?

Я считаю, что намерение этого иначе потенциально двусмысленного правила ясно показано новой формулировкой на С++ 11, которая разрешает все:

[C++11: 9.4.2/6]: Элементы статических данных инициализируются и уничтожаются точно так же, как нелокальные переменные (3.6.2, 3.6.3).

[C++11: 3.6.2/4]: Определяется реализацией, выполняется ли динамическая инициализация нелокальной переменной со статической продолжительностью хранения до первого утверждения main. [..]

Ответ 2

С++ 03: Короче говоря, никаких гарантий

С++ 11: нет гарантии, см. ответ Легкость.

Моя интерпретация/анализ операторов С++ 03:


Терминология: [basic.start.init]/1

Инициализация нуля и инициализация с постоянным выражением коллективно называются статической инициализацией; вся другая инициализация - это динамическая инициализация.


Порядок инициализации для нелокальных объектов:

Объекты со статической продолжительностью хранения (3.7.1) должны быть инициализированы нулями (8.5), прежде чем произойдет любая другая инициализация.

Но он не упоминает, когда происходит "любая другая инициализация", т.е. нет гарантии, что это будет до первого оператора main, даже для нулевой инициализации.

Объекты типов POD (3.9) со статической продолжительностью хранения, инициализированными постоянными выражениями (5.19), должны быть инициализированы до начала любой динамической инициализации.

Но опять же никаких гарантий.


Динамическая инициализация

[basic.start.init]/3

Определяется реализацией независимо от того, выполняется ли динамическая инициализация (8.5, 9.4, 12.1, 12.6.1) объекта пространства пространства имен до первого утверждения main. Если инициализация отложена до некоторого момента времени после первого утверждения main, она должна произойти до первого использования какой-либо функции или объекта, определенного в той же самой единицы перевода, что и объект, который будет инициализирован.

Но что такое "объект пространства имен"? Я не нашел четкого определения в Стандарте. scope на самом деле является свойством имени, а не объекта. Поэтому мы могли бы прочитать это как "объект, определенный в области пространства имен" или "объект, введенный именем области пространства имен". Обратите внимание на ссылку "9.4" после динамической инициализации. Он относится к "Статическим членам", который может означать только статические элементы данных. Поэтому я бы сказал, что это означает "объект, определенный в области пространства имен", поскольку статические члены данных определены в области пространства имен:

[class.static.data]/2

Определение для статического члена данных должно появиться в области пространства имен, охватывающей определение класса членов.

Даже если вы не согласны с этой интерпретацией, [Basic.start.init]/1

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

Это явно относится к элементам статических данных, что означает, что они не могут быть инициализированы иначе, чем объекты, введенные именами области пространства имен, если есть такой объект перед определением статического элемента данных. То есть, если не было никакой гарантии при динамической инициализации статических членов данных, будут применяться гарантии любого предшествующего объекта, введенного именем области пространства имен, которые являются: none (его не нужно инициализировать до первого утверждение основного).

Если нет такого объекта, который предшествует определению статического члена данных и, вы не согласны с интерпретацией - не было бы никакой гарантии по динамической инициализации статических элементов данных вообще.


Заключение

Таким образом, у нас есть только гарантия, что динамическая инициализация происходит где-то (до любого использования) плюс исключение, при котором инициализация с побочными эффектами не должна устраняться. Тем не менее, мы не гарантируем, что перед первым выражением main выполняется любая инициализация нелокальных объектов.


Примечание. Существуют обходные пути, например:

#include <iostream>

struct my_class
{
    static int& my_var()
    {
        static int i = 42;
        return i;
    }
};

int j = ++my_class::my_var();
int k = ++my_class::my_var();

int main()
{
    std::cout << j << " : " << k << std::endl;
}