Совокупная инициализация структуры с использованием собственных членов данных
Это n-й вопрос по этому поводу, но я не смог найти точную копию...
Предположим, следующий код:
#include <iostream>
struct S {
int x;
int y;
};
class C {
public:
S s;
C() : s{123, s.x} {}
};
int main() {
std::cout << C().s.y << '\n';
}
Это нормально для инициализации s.y
как это? (только JetBrains' ReSharper жалуется на это со следующим: Object member this->s.x might not be initialized
).
Было бы здорово, если бы кто-то подтвердил свой ответ цитатой из стандарта.
2 ответа
Из C++ 14
8.5.1 Агрегаты [dcl.init.aggr]
1 Агрегат - это массив или класс (раздел 9) без предоставленных пользователем конструкторов (12.1), без закрытых или защищенных нестатических элементов данных (пункт 11), без базовых классов (пункт 10) и без виртуальных функций (10,3).
2 Когда агрегат инициализируется списком инициализаторов, как указано в 8.5.4, элементы списка инициализаторов берутся в качестве инициализаторов для элементов агрегата в порядке возрастания индекса или элемента.
Это означает, что sx сначала инициализируется с 123, затем sy инициализируется с sx
Без оптимизации GCC 6.3 генерирует
C::C():
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi
mov rax, QWORD PTR [rbp-8] # read address of s
mov DWORD PTR [rax], 123 # write 123 to s.x (offset 0 from s)
mov rax, QWORD PTR [rbp-8] # read address of s again
mov edx, DWORD PTR [rax] # read contents of s.x to edx
mov rax, QWORD PTR [rbp-8] # read address of s
mov DWORD PTR [rax+4], edx # write s.y (offset 4 from s)
nop
pop rbp
ret
Что согласуется с тем, что говорится в стандартах.
Хотя может показаться, что не существует правила, прямо заявляющего о том, что этот трюк плохо сформирован, ему недостаточно иметь четко определенное поведение.
Я думаю, что есть некоторые проблемы с порядком оценки:
это правило определяет порядок вычисления выражений в ограниченном списке; Конечно, есть и порядок инициализации членов.
Можно с уверенностью сказать, что каждый член структуры инициализируется после вычисления соответствующего выражения в списке в скобках (очевидно, s.x
в скобочном списке оценивается перед инициализацией s.y
).
Однако, похоже, нет правила, которое бы гласило, что s.x
в вашем случае его нужно инициализировать перед вычислением второго элемента в фигурном списке, например, программа может вычислить все выражения в списке в скобках, прежде чем даже начинать инициализацию полей структуры.
Конечно, отсутствие правила нелегко доказать, но если его там нет, оно выглядит как UB.
UPD: правило из ответа @PaulFloyd действительно очень напоминает то, что отсутствовало в моем ответе, возможно, это не UB в конце концов.