Почему мои члены структуры не были правильно инициализированы с помощью `{}`?

У меня был следующий код:

#include <iostream>

struct T
{
   int a, b, c;
};

int main()
{
   T t = {0};
   std::cout << t.a << ',' << t.b << ',' << t.c << '\n';
}

Выход:

0,0,0

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

Итак, я изменил {0} на {1}:

#include <iostream>

struct T
{
   int a, b, c;
};

int main()
{
   T t = {1};
   std::cout << t.a << ',' << t.b << ',' << t.c << '\n';
}

Результат:

1,0,0

Я ожидал 1,1,1 вместо этого.

Почему мои члены struct не все правильно инициализируются?

Ответ 1

Когда вы пишете = {0}, это только явно инициализирует первый член; остальные нулевые инициализируются неявно в соответствии со стандартом, поэтому на первый взгляд кажется, что вы явно инициализировали все члены с 0, которые вы написали, но вы этого не сделали.

То место, где вы написали 0, влияет только на первого члена. Поэтому, когда однажды вы изменили его на 1, думая, что он изменит всех членов, у вас будет ошибка, как здесь. Это вводящий в заблуждение/опасный/глупый/хрупкий код.

По этой причине, без сопроводительного пояснительного комментария, = {0} не будет проходить проверку кода в моей команде. Вы должны изначально написать:

T t = {};

И теперь, чтобы решить вашу проблему в соответствии с новыми требованиями, вы должны написать:

T t = {1,1,1};

или, если вы не возражаете против своего struct потенциально теряющего POD-ness, укажите T конструктор.


Формальная формулировка

[C++11: 8.5.1/2]: Когда агрегат инициализируется списком инициализаторов, как указано в 8.5.4, элементы списка инициализаторов берутся как инициализаторы для членов агрегата, увеличивая индекс или порядок членов. Каждый член инициализируется копией из соответствующего предложения инициализатора. Если выражение initializer-выражение является выражением, и для преобразования выражения требуется суживающее преобразование (8.5.4), программа плохо сформирована. [..]

[C++11: 8.5.1/6]: Список инициализаторов плохо сформирован, если число инициализационных-команд превышает число элементов или элементов для инициализации.

[C++11: 8.5.1/7]: Если в списке меньше элементов-инициализаторов, чем в агрегате, то каждый член, явно не инициализированный, должен быть инициализирован из пустого списка инициализаций (8.5.4).