Here are 8 ways to declare and initialize arrays in C++11 that seems ok under g++:
/*0*/ std::array<int, 3> arr0({1, 2, 3});
/*1*/ std::array<int, 3> arr1({{1, 2, 3}});
/*2*/ std::array<int, 3> arr2{1, 2, 3};
/*3*/ std::array<int, 3> arr3{{1, 2, 3}};
/*4*/ std::array<int, 3> arr4 = {1, 2, 3};
/*5*/ std::array<int, 3> arr5 = {{1, 2, 3}};
/*6*/ std::array<int, 3> arr6 = std::array<int, 3>({1, 2, 3});
/*7*/ std::array<int, 3> arr7 = std::array<int, 3>({{1, 2, 3}});
What are the correct ones according to the strict standard (and the upcoming C++14 standard) ? What are the most common/used and those to avoid (and for what reason) ?
C++11 summary / TL;DR
std::arraycontains a raw array. Therefore, examples 1, 3, 5, 7 are not required to work. However, I do not know of a Standard Library implementation where they do not work (in practice).std::array<int, 3> arr4 = {1, 2, 3};I'd prefer version 4 or version 2 (with the brace elision fix), since they initialize directly and are required/likely to work.
For Sutter's AAA style, you can use
auto arrAAA = std::array<int, 3>{1, 2, 3};, but this requires the brace elision fix.std::arrayis required to be an aggregate [array.overview]/2, this implies it has no user-provided constructors (i.e. only default, copy, move ctor).An initialization with
(..)is direct-initialization. This requires a constructor call. In the case ofarr0andarr1, only the copy/move constructor are viable. Therefore, those two examples mean create a temporarystd::arrayfrom the braced-init-list, and copy/move it to the destination. Through copy/move elision, the compiler is allowed to elide that copy/move operation, even if it has side effects.N.B. even though the temporaries are prvalues, it might invoke a copy (semantically, before copy elision) as the move ctor of
std::arraymight not be implicitly declared, e.g. if it were deleted.These are examples of copy-initialization. There are two temporaries created:
{1, 2, 3}to call the copy/move constructorstd::array<int, 3>(..)the latter temporary then is copied/moved to the named destination variable. The creation of both temporaries can be elided.
As far as I know, an implementation could write an(This possibility is ruled out by [container.requirements.general], kudos to David Krauss, see this discussion.)explicit array(array const&) = default;constructor and not violate the Standard; this would make those examples ill-formed.This is aggregate-initialization. They all "directly" initialize the
std::array, without calling a constructor ofstd::arrayand without (semantically) creating a temporary array. The members of thestd::arrayare initialized via copy-initialization (see below).On the topic of brace-elision:
In the C++11 Standard, brace elision only applies to declarations of the form
T x = { a };but not toT x { a };. This is considered a defect and will be fixed in C++1y, however the proposed resolution is not part of the Standard (DRWP status, see top of the linked page) and therefore you cannot count on your compiler implementing it also forT x { a };.Therefore,
std::array<int, 3> arr2{1, 2, 3};(examples 0, 2, 6) are ill-formed, strictly speaking. As far as I know, recent versions of clang++ and g++ allow the brace elision inT x { a };already.In example 6,
std::array<int, 3>({1, 2, 3})uses copy-initialization: the initialization for argument passing is also copy-init. The defective restriction of brace elision however, "In a declaration of the formT x = { a };", also disallows brace elision for argument passing, since it's not a declaration and certainly not of that form.On the topic of aggregate-initialization:
As Johannes Schaub points out in a comment, it is only guaranteed that you can initialize a
std::arraywith the following syntax [array.overview]/2:array<T, N> a = { initializer-list };You can deduce from that, if brace-elision is allowed in the form
T x { a };, that the syntaxarray<T, N> a { initializer-list };is well-formed and has the same meaning. However, it is not guaranteed that
std::arrayactually contains a raw array as its only data member (also see LWG 2310). I think one example could be a partial specializationstd::array<T, 2>, where there are two data membersT m0andT m1. Therefore, one cannot conclude thatarray<T, N> a {{ initializer-list }};is well-formed. This unfortunately leads to the situation that there's no guaranteed way of initializing a
std::arraytemporary w/o brace elision forT x { a };, and also means that the odd examples (1, 3, 5, 7) are not required to work.All of these ways to initialize a
std::arrayeventually lead to aggregate-initialization. It is defined as copy-initialization of the aggregate members. However, copy-initialization using a braced-init-list can still directly initialize an aggregate member. For example:The first tries to initialize the array elements from the initializer-clauses
1and2, respectively. This copy-initialization is equivalent tofoo arr0_0 = 1;which in turn is equivalent tofoo arr0_0 = foo(1);which is illegal (deleted copy-ctor).The second does not contain a list of expressions, but a list of initializers, therefore it doesn't fulfil the requirements of [array.overview]/2. In practice,
std::arraycontains a raw array data member, which would be initialized (only) from the first initializer-clause{1}, the second clause{2}then is illegal.The third has the opposite problem as the second: It works if there is an array data member, but that isn't guaranteed.