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

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

int arr[10] = {};

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

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

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

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

предупреждение: отсутствует инициализатор для члена 'std:: array:: _ M_elems'

3 ответа

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

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

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

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

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

[...] Конечно, синтаксис C++: 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}', так как инициализатор пустых скобок не поддерживается языком (вы получаете предупреждение с -pedantic); в C++ вы можете написать ' = {}' или 'T foo = T();', но вам не нужно специально писать ' = {0}'.

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

Мы также можем увидеть эту тему в списках разработчиков Clang в thead: -Wmissing-field-initializer.

Для справки проект стандартного раздела C++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. - конец примера]

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

Во-первых, вы можете использовать ({}) инициализатор с 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] = {};Это заставляет меня задуматься, почему ты не попробовал это сначала. Почему вы решили использовать ({}) вместо = {} когда вы построили std::array версия = {} синтаксис?

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

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

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

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

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

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