Ответ 1
С++ 11 summary/TL; DR
- Из-за дефекта дефекта скобки, примеры 0, 2, 6 не должны работать. Однако последняя версия компиляторов реализует предлагаемую резолюцию для этого дефекта, чтобы эти примеры работали.
- Как не указано, содержит ли
std::array
необработанный массив. Поэтому примеры 1, 3, 5, 7 не должны работать. Однако я не знаю о реализации стандартной библиотеки, где они не работают (на практике). - Пример 4 всегда будет работать:
std::array<int, 3> arr4 = {1, 2, 3};
Я бы предпочел версию 4 или версию 2 (с исправлением исправления), поскольку они инициализируются напрямую и требуются/могут работать.
Для стиля Sutter AAA вы можете использовать auto arrAAA = std::array<int, 3>{1, 2, 3};
, но для этого требуется исправление исправления фигур.
std::array
требуется быть агрегатом [array.overview]/2, это означает, что у него нет конструкторов, предоставляемых пользователем (т.е. только по умолчанию, копировать, перемещать ctor).
std::array<int, 3> arr0({1, 2, 3});
std::array<int, 3> arr1({{1, 2, 3}});
Инициализация с помощью (..)
является прямой инициализацией. Для этого требуется вызов конструктора. В случае arr0
и arr1
только конструктор copy/move жизнеспособны. Следовательно, эти два примера означают создание временного std::array
из списка braced-init и копирование/перенос его в пункт назначения. С помощью copy/move elision компилятор может исключить эту операцию копирования/перемещения, даже если он имеет побочные эффекты.
<суб > N.B. хотя временные значения являются prvalues, он может вызывать копию (семантически, до копирования), поскольку перемещение ctor std::array
может быть неявно объявлено, например. если он был удален.
std::array<int, 3> arr6 = std::array<int, 3>({1, 2, 3});
std::array<int, 3> arr7 = std::array<int, 3>({{1, 2, 3}});
Это примеры инициализации копирования. Созданы два временных файла:
- через скопированный-init-list
{1, 2, 3}
, чтобы вызвать конструктор copy/move - через выражение
std::array<int, 3>(..)
последний временный затем копируется/перемещается в указанную целевую переменную. Создание обоих временных времен может быть отменено.
Насколько мне известно, реализация может написать конструктор (Эта возможность исключена [container.requirements.general], прислугой Дэвида Краусса, см. это обсуждение.)explicit array(array const&) = default;
и не нарушать стандарт; это приведет к тому, что эти примеры плохо сформированы.
std::array<int, 3> arr2{1, 2, 3};
std::array<int, 3> arr3{{1, 2, 3}};
std::array<int, 3> arr4 = {1, 2, 3};
std::array<int, 3> arr5 = {{1, 2, 3}};
Это агрегатная инициализация. Все они "прямо" инициализируют std::array
, не вызывая конструктор std::array
и без (семантически) создавая временный массив. Элементы std::array
инициализируются с помощью инициализации копирования (см. Ниже).
По теме brace-elision:
В стандарте С++ 11, выравнивание фигурной скобки применяется только к объявлениям формы T x = { a };
, но не к T x { a };
. Это считается дефектом и будет исправлено в С++ 1y, однако предлагаемая резолюция не является частью стандарта (статус DRWP, см. вверху связанной страницы), и поэтому вы не можете рассчитывать на свой компилятор, реализующий его также для T x { a };
.
Следовательно, std::array<int, 3> arr2{1, 2, 3};
(примеры 0, 2, 6), строго говоря, плохо сформированы. Насколько мне известно, недавние версии clang++ и g++ уже позволяют использовать элемент T x { a };
.
В примере 6, std::array<int, 3>({1, 2, 3})
использует инициализацию копирования: инициализация для передачи аргумента также является copy-init. Однако дефектное ограничение выбора фигурной скобки: "В объявлении формы T x = { a };
" также запрещает выравнивание фигуры для передачи аргумента, поскольку оно не является объявлением и, конечно, не имеет такой формы.
По теме инициализации агрегата:
Как Йоханнес Шауб указывает в комментарии, гарантируется, что вы можете инициализировать std::array
со следующим синтаксисом [array.overview]/2:
array<T, N> a = { initializer-list };
Вы можете вывести из этого, , если brace-elision разрешено в форме T x { a };
, что синтаксис
array<T, N> a { initializer-list };
хорошо сформирован и имеет то же значение. Однако не гарантируется, что std::array
на самом деле содержит необработанный массив в качестве единственного члена данных (см. Также LWG 2310). Я думаю, что одним примером может быть частичная специализация std::array<T, 2>
, где есть два элемента данных T m0
и T m1
. Поэтому нельзя сделать вывод, что
array<T, N> a {{ initializer-list }};
хорошо сформирован. Это, к сожалению, приводит к тому, что нет гарантированного способа инициализации временного отсутствия std::array
для T x { a };
, а также означает, что нечетные примеры (1, 3, 5, 7) не требуются для работы.
Все эти способы инициализации std::array
в конечном итоге приводят к агрегационной инициализации. Он определяется как копирование инициализации совокупных членов. Тем не менее, инициализация копирования с использованием скопированного списка-init может по-прежнему напрямую инициализировать член агрегата. Например:
struct foo { foo(int); foo(foo const&)=delete; };
std::array<foo, 2> arr0 = {1, 2}; // error: deleted copy-ctor
std::array<foo, 2> arr1 = {{1}, {2}}; // error/ill-formed, cannot initialize a
// possible member array from {1}
// (and too many initializers)
std::array<foo, 2> arr2 = {{{1}, {2}}}; // not guaranteed to work
Первая попытка инициализировать элементы массива из предложений-инициализаторов 1
и 2
, соответственно. Эта инициализация копии эквивалентна foo arr0_0 = 1;
, которая в свою очередь эквивалентна foo arr0_0 = foo(1);
, которая является незаконной (удаленная копия-ctor).
Второй не содержит список выражений, но список инициализаторов, поэтому он не удовлетворяет требованиям [array.overview]/2. На практике std::array
содержит элемент данных необработанного массива, который будет инициализирован (только) из первого предложения-инициализатора {1}
, второе предложение {2}
затем является незаконным.
Третий имеет противоположную проблему как вторую: он работает, если есть элемент данных массива, но это не гарантируется.