Увеличивает ли приращение в списке инициализатора члена неопределенное поведение?

Является ли это причиной неопределенного поведения? В частности, приращение в списке инициализаторов и как это будет оцениваться.

class Wrinkle {
public:
    Wrinkle(int i) : a(++i), b(++i), x(++i) {}
private:
    int a;
    int x;
    int b;
};

Разница в порядке между объявлением участников и списком инициализаторов предназначена, поскольку это пример, который продемонстрировал бы именно эту разницу, поэтому, пожалуйста, игнорируйте ее пока.

Ответы

Ответ 1

Это не генерирует неопределенное поведение, потому что:

[class.base.init] # 7

[Примечание: инициализация, выполняемая каждым mem-инициализатором, представляет собой полное выражение. Любое выражение в mem-инициализаторе оценивается как часть полного выражения, которое выполняет инициализацию. ]

[intro.execution]

5. Полное выражение

  • [...]
  • init-declarator или mem-initializer, включая составляющие выражения инициализатора,

9. Каждое вычисление значения и побочный эффект, связанные с полным выражением, секвенируются перед каждым вычислением значения и побочным эффектом, связанным со следующим полным выражением, которое должно быть оценено.


Но будьте осторожны:

[class.base.init] # 13

В конструкторе без делегирования инициализация выполняется в следующем порядке:

  • [...]

  • Затем нестатические члены данных инициализируются в том порядке, в котором они были объявлены в определении класса (опять же, независимо от порядка mem-инициализаторов).

Таким образом, ваш код будет эффективно присваивать i + 1 a, i + 2 x и i + 3 b.

Ответ 2

Стандарт С++ 17 содержит пример, почти такой же, как в вопросе:

struct B1 { B1(int); /* ... */ };
struct B2 { B2(int); /* ... */ };
struct D : B1, B2 {
  D(int);
  B1 b;
  const int c;
};

D::D(int a) : B2(a+1), B1(a+2), c(a+3), b(a+4) { /* ... */ }
D d(10);

За этим следует примечание:

[Примечание: инициализация, выполняемая каждым mem-инициализатором, представляет собой полное выражение (4.6). Любое выражение в mem-инициализаторе оценивается как часть полного выражения, которое выполняет инициализацию. - конечная нота]

Следуя ссылке, раздел 4.6 говорит нам, что одно из определений "полного выражения"

... mem-initializer, включая составляющие выражения инициализатора,

Фраза "включая составляющие выражения инициатива" настоятельно указывает мне, что вышеуказанный код является законным, потому что побочные эффекты ++i будут выполнены до перехода к следующему инициализатору. Это только мое чтение стандарта, хотя, я рад отложить все, кто имеет более стандартный опыт, чем я.

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