Инициализация в классе от статического члена того же типа
Является ли следующий код допустимым, например, не приводит к неопределенному поведению?
struct S
{
int i = s.i;
static S s;
};
S S::s;
int main()
{
S a; // a.i = 0
S::s.i = 42;
S b; // b.i = 42
}
Насколько я знаю, все переменные со статической длительностью хранения инициализируются нулями. следовательно s.i
является 0
на S::s
творение и все хорошо. Но, может быть, я что-то упустил.
2 ответа
Я бы сказал, что это хорошо определено.
Статические члены данных инициализируются и уничтожаются точно так же, как нелокальные переменные.
[basic.start.static] / 2 (выделено мое)
Инициализатор констант для переменной или временного объекта o является инициализатором, чье полное выражение является константным выражением, за исключением того, что, если o является объектом, такой инициализатор может также вызывать конструкторы constexpr для o и его подобъектов, даже если эти объекты не являются Типы литеральных классов. [Примечание: у такого класса может быть нетривиальный деструктор. - примечание конца] Инициализация константы выполняется, если переменная или временный объект со статическим или потоковым сроком хранения инициализируется константным инициализатором для объекта. Если постоянная инициализация не выполняется, переменная со статической продолжительностью хранения или продолжительностью хранения потока инициализируется нулями. Вместе нулевая инициализация и постоянная инициализация называются статической инициализацией; все остальные инициализации - это динамическая инициализация. Вся статическая инициализация сильно происходит до ([intro.races]) любой динамической инициализации. [Примечание: динамическая инициализация нелокальных переменных описана в [basic.start.dynamic]; что из локальных статических переменных описано в [stmt.dcl]. - конец примечания]
[dcl.init] / 6 (выделено мое)
Инициализация нуля объекта или ссылки типа T означает:
- если T является скалярным типом, объект инициализируется значением, полученным путем преобразования целочисленного литерала 0 (ноль) в T;
- если T является (возможно, квалифицированным по cv) типом класса, не являющимся объединением, каждый нестатический член данных, каждый подобъект не-виртуального базового класса и, если объект не является подобъектом базового класса, каждый подобъект виртуального базового класса равен нулю -инициализируется и заполнение инициализируется нулевыми битами;
- если T является (возможно, cv-квалифицированным) типом объединения, первый нестатический именованный элемент данных объекта инициализируется нулями, а заполнение инициализируется нулевыми битами;
- если T является типом массива, каждый элемент инициализируется нулями;
- если T является ссылочным типом, инициализация не выполняется.
Так как int i = s.i;
средства s.i
проходит динамическую инициализацию, она гарантированно инициализируется нулями заранее. Поэтому, когда он будет использован для последующей инициализации, его значение не будет неопределенным. 0 следует ожидать.
Вы что-то упускаете. Переменные со статической продолжительностью хранения обнуляются, а затем вызывается их конструктор.
Я не могу сказать, является ли инициализация S.i
со значением S.i
неопределенное поведение (потому что S.i
не инициализируется в этой точке) или нет (потому что он должен быть нулевым).
Изменить: код в отчете о дефектах 2026 очень похож на это, и объявлен неправильно сформированным (что означает, что компилятор должен выдать ошибку). Я подозреваю, что намерение комитета состоит в том, что код ОП является неопределенным поведением.
Изменить 2: вышеупомянутый DR относится к constexpr
ценности. Это, вероятно, меняет вещи достаточно, что не имеет значения.
Сказав это: если вы полагаетесь на очень тщательное чтение стандарта, чтобы сделать свой код легальным, вы полагаетесь на то, что автор компилятора прочитал его так же внимательно. Вы можете быть правы, но это не поможет в краткосрочной перспективе, если автор компилятора неправильно прочитал и реализовал что-то еще (хотя, надеюсь, они в конечном итоге исправят ошибку).