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

Это 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 в конце концов.

Другие вопросы по тегам