Почему я могу инициализировать регулярный массив из {}, но не std:: array

Это работает:

int arr[10] = {};

Все элементы arr инициализируются значением до нуля.

Почему это не работает:

std::array<int, 10> arr({}); 

Я получаю следующее предупреждение от g++ (версия 4.8.2):

предупреждение: отсутствует инициализатор для элемента 'std:: array < int, 10ul > :: _ M_elems

Ответ 1

Есть два вопроса, один из которых является вопросом стиля и предупреждения.

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

std::array<int, 10> arr = {}; 

Хотя это все еще оставляет предупреждение.

Предупреждение распространяется на отчет об ошибке gcc: - запрос релаксации инициализатора поля -Wmissing-field, и в одном из комментариев говорится:

[...] Конечно, синтаксис С++, говорящий MyType x = {}; следует поддерживать, как показано здесь:

http://en.cppreference.com/w/cpp/language/aggregate_initialization

где, например:

struct S {
  int a;
  float b;
  std::string str;
};

S s = {}; // identical to S s = {0, 0.0, std::string};

Это не следует предупреждать по причинам, изложенным в предыдущих комментариях.

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

Мое утверждение об нулевой инициализации было неточным (спасибо), но общий момент все еще стоит: в C вы должны написать '= {0}', так как Инициализатор пустой скобки не поддерживается языком (вы получаете предупреждение с -панией); в С++ вы можете написать '= {}' или 'T foo = T(); ', но вам не нужно писать' = {0} 'конкретно.

Последние версии gcc не выдают это предупреждение для этого случая, видеть его вживую, работая с gcc 5.1.

Мы видим, что эта тема также рассматривается в списках разработчиков Clang Developers в thead: - инициализаторы Wmissing-field.

Для справки в стандартном разделе проекта С++ 11 8.5.1 [dcl.init.aggr] говорится:

Если в списке меньше предложений инициализатора, чем членов в совокупности, то каждый член явно не инициализирован должны быть инициализированы из пустого списка инициализаторов (8.5.4). [ Пример:

struct S { int a; const char* b; int c; };
S ss = { 1, "asdf" };

инициализирует ss.a с 1, ss.b с "asdf" и ss.c со значением выражение формы int(), то есть 0. -end example]

Так как это допустимо С++, хотя, как указано, с помощью {} недействительно C99. Можно утверждать, что это всего лишь предупреждение, но это похоже на идиоматический С++, использующий {} для агрегатной инициализации и проблематично, если мы используем -Werror, чтобы превращать предупреждения в ошибки.

Ответ 2

Во-первых, вы можете использовать инициализатор ({}) с объектом std::array, но семантически это означает прямую инициализацию с использованием конструктора-копии из временного инициализированного значения объекта std::array, то есть он эквивалентен

std::array<int, 10> arr(std::array<int, 10>{}); 

И он должен быть скомпилирован.

Во-вторых, вам действительно не нужно идти ({}), когда вы можете просто сделать

std::array<int, 10> arr = {};

или

std::array<int, 10> arr{};

Первый из двух наиболее синтаксически похож на ваш int arr[10] = {};, что заставляет меня задаться вопросом, почему вы сначала не пробовали его. Почему вы решили использовать ({}) вместо = {}, когда вы построили синтаксис = {} = {}?

Ответ 3

Достаточно людей указали это как "проблему" при компиляции с -Werror, что, я думаю, стоит упомянуть, что проблема уходит, если вы просто удвоились:

std::array<int, 10> arr{{}};

Не создает никаких предупреждений для меня по gcc 4.9.2.

Чтобы добавить немного о том, почему это его решает: я понял, что std:: array на самом деле является классом с массивом C как его единственным членом. Таким образом, двойное увеличение на фигурных скобках имеет смысл: внешние фигурные скобки указывают, что вы инициализируете класс, а затем внутренние фигурные скобки по умолчанию инициализируют один и только член класса.

Поскольку нет никакой двусмысленности, когда в классе есть только одна переменная, просто использование одной пары {} должно быть разумным, но gcc здесь слишком педантичен и предупреждает.