Инициализация нелокальных не встроенных переменных: происходит ли это строго перед вызовом функции main ()? - PullRequest
0 голосов
/ 13 января 2019

Определен ли порядок печати цифр в следующей простой реализации программы?

#include <iostream>


struct Foo
{
    Foo()
    {
        std::cout << "1" << std::endl;
    }
};

Foo foo;


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

Некоторые формулировки из стандарта ( Динамическая инициализация нелокальных переменных [basic.start.dynamic] / 4 ):

Определяется реализацией, выполняется ли динамическая инициализация нелокальной, не встроенной переменной со статической длительностью хранения перед первым оператором main или откладывается. Если оно откладывается, то это происходит до того, как любое использование неинициализации odr любой не встроенной функции или не встроенной переменной, определенной в той же единице перевода, что и инициализируемая переменная. *

...

*) Нелокальная переменная со статической продолжительностью хранения, имеющая инициализацию с побочными эффектами, инициализируется в этом случае, даже если она сама не используется odr ([basic.def.odr], [basic.stc.static] ).

И функция main() не используется odr .

1 Ответ

0 голосов
/ 13 января 2019

Допустимые выходы: 1/2, 2 и 2 / 1.

Если инициализация переменной foo имеет значение , а не отложено, то она упорядочивается до начала main, поэтому 1 печатается до 2.

Если инициализация откладывается, то требуется, чтобы инициализация foo происходила до (любого) odr-использования из foo. Это не говорит о том, что инициализация не должна происходить, если не используется odr. Вывод 2/1 определенно будет очень странным для этого примера (а вывод 2 - единственный, используемый на практике реализациями, которые откладывают инициализацию), но я не вижу в стандарте ничего, что строго исключало бы его.

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

…
Foo foo;

struct Bar {
  Bar() { std::cout << "3\n"; }
  void Use() {}
} bar;


int main()
{
    std::cout << "2" << std::endl;
    bar.Use();
}

С одной защитой и отложенной инициализацией foo будет инициализирован вместе с bar, даже если в программе нет использования odr (кроме инициализации) foo. В этом случае это также требуется для согласованности, поскольку в примере используется упорядоченная инициализация , поэтому инициализация foo должна быть упорядочена до bar, поэтому единственные допустимые выходы - 1/3/2 ( без отложенной инициализации) и 2/1/3 (отложенная инициализация). Но если бы мы использовали другую конструкцию для получения неупорядоченной инициализации, реализация также могла бы выдать 2/3/1 (опять же, без использования инициализации odr foo).

...