Почему я могу инициализировать обычный массив из {}, но не 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 слишком педантичен и предупреждает.