Подтвердить что ты не робот

Статическая инициализация С++ vs __attribute __ ((конструктор))

Пример:

struct Foo { Foo() { printf("foo\n"); } };
static Foo foo;

__attribute__((constructor)) static void _bar() { printf("bar\n"); }

Является ли оно первым детерминированным шрифтом foo или bar?

(Я надеюсь и буду ожидать, что конструкторы статических объектов всегда будут выполняться сначала, но не уверены, а документ GCCs об атрибуте конструктора ничего не говорит об этом.)

4b9b3361

Ответ 1

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

Кстати, __attribute__((constructor)) не является стандартным С++. Это расширение GCC. Поэтому поведение вашей программы зависит от того, как GCC определил ее. Короче говоря, она определяется реализацией, в соответствии с ней сначала печатается foo.

doc говорит,

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

Вы можете указать необязательный целочисленный приоритет для управления порядком, в котором выполняются функции конструктора и деструктора. Конструктор с меньшим номером приоритета запускается перед конструктором с большим номером приоритета; противоположное соотношение имеет место для деструкторов. Итак, если у вас есть конструктор, который выделяет ресурс и деструктор, который освобождает один и тот же ресурс, обе функции обычно имеют одинаковый приоритет. Приоритеты для функций конструктора и деструктора такие же, как те, которые указаны для объектов С++ для пространства имен (см. атрибуты С++).

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

Думаю, вам также хотелось бы прочитать следующее:

Если вы хотите контролировать/изменять порядок инициализации, вы можете использовать атрибут init_priority, предоставляя приоритет. Взято с страницы:

Some_Class  A  __attribute__ ((init_priority (2000)));
Some_Class  B  __attribute__ ((init_priority (543)));

Здесь B инициализируется до A.

Ответ 2

Это кажется недетерминированным. У меня также был foo\nbar\n как результат примера в моем вопросе при компиляции с GCC. Однако при компиляции с LLVM/Clang я получаю bar\nfoo\n.

Но, поскольку я не уверен, что это может быть ошибкой в ​​Clang, я заполнил отчет об ошибке здесь. Редактировать: у меня есть ответ, и это похоже на ошибку в Clang, которая еще не исправлена. Не уверен, что делать с этим. Ожидаемое и должно быть поведение действительно детерминировано здесь, однако вы не можете зависеть от него, поскольку есть хотя бы один главный компилятор (Clang), который делает это неправильно (или отличается от GCC, если мы принимаем это как спецификацию для __attribute__((constructor))).

Обратите внимание, что это может быть действительно актуальным и важным в коде реального мира. Например. здесь - пример, который засекает случайный генератор, который не работает с Clang.