Совокупная инициализация структуры с использованием собственных элементов данных

Это 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).

Было бы здорово, если бы кто-то подтвердил свой ответ цитатой из стандарта.

Ответ 1

Из С++ 14

8.5.1 Агрегаты [dcl.init.aggr]

1 Агрегат - это массив или класс (раздел 9) без каких-либо созданных пользователем конструкторов (12.1), без личных или защищенные нестатические элементы данных (раздел 11), базовые классы (раздел 10) и виртуальные функции (10.3).

2 Когда агрегат инициализируется списком инициализаторов, как указано в 8.5.4, элементы списка инициализаторов берутся как инициализаторы для членов совокупности, увеличивая индекс или порядок членов.

Это означает, что s.x сначала инициализируется 123, тогда s.y инициализируется с помощью s.x.

Без оптимизации 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

Что согласуется с тем, что говорят стандарты.

Ответ 2

Хотя казалось бы, что нет правила, которое явно заявляет, что этот трюк плохо сформирован, недостаточно для него иметь четко определенное поведение.

Я думаю, что у него есть некоторые проблемы с порядком оценки:

это правило определяет порядок оценки выражений в скобках; Конечно, есть и порядок инициализации члена.

Можно с уверенностью сказать, что каждый элемент структуры инициализируется после оценки соответствующего выражения в списке скобок (очевидно, s.x в скобках списка оценивается до инициализации s.y).

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

Конечно, отсутствие правила непросто доказать, но если оно не существует, оно выглядит как UB.

UPD: правило из ответа @PaulFloyd действительно очень похоже на то, чего не было в моем ответе, возможно это не UB в конце концов.