87

Code:

std::vector<int> x{1,2,3,4};
std::array<int, 4> y{{1,2,3,4}};

Why do I need double curly braces for std::array?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sungmin
  • 2,499
  • 3
  • 26
  • 32

2 Answers2

74

std::array<T, N> is an aggregate: it doesn't have any user-declared constructors, not even one taking a std::initializer_list. Initialization using braces is performed using aggregate initialization, a feature of C++ that was inherited from C.

The "old style" of aggregate initialization uses the =:

std::array<int, 4> y = { { 1, 2, 3, 4 } };

With this old style of aggregate initialization, extra braces may be elided, so this is equivalent to:

std::array<int, 4> y = { 1, 2, 3, 4 };

However, these extra braces may only be elided "in a declaration of the form T x = { a };" (C++11 §8.5.1/11), that is, when the old style = is used . This rule allowing brace elision does not apply for direct list initialization. A footnote here reads: "Braces cannot be elided in other uses of list-initialization."

There is a defect report concerning this restriction: CWG defect #1270. If the proposed resolution is adopted, brace elision will be allowed for other forms of list initialization, and the following will be well-formed:

std::array<int, 4> y{ 1, 2, 3, 4 };

(Hat tip to Ville Voutilainen for finding the defect report.)

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 3
    So it's a failure of the abstraction model being presented for `array`? – Mark Ransom Jul 09 '12 at 17:35
  • 1
    @Mehrdad: It *is* uniform. What *isn't* uniform is the fact that you're initializing two completely different types. – Nicol Bolas Jul 09 '12 at 17:35
  • 5
    @NicolBolas: I thought the whole point of uniformity was to use the same initialization syntax for different types? (Yes, I do understand what's happening... I'm just saying it's not "uniform" to the user, regardless of whether there's an explanation for it.) – user541686 Jul 09 '12 at 17:36
  • @MarkRansom: Well, it's rather a quirk of the language, because `std::array y = {1,2,3,4};` works with a *warning* from Clang *suggesting* braces, instead of a hard error about not being allowed to "omit braces around initialization of subobject when using direct list-initialization". – Xeo Jul 09 '12 at 17:38
  • 1
    @Xeo: Actually, brace elision is permitted in aggregate initialization, but (apparently) not when the direct list initialization syntax is used. – James McNellis Jul 09 '12 at 18:01
  • However, the standard doesn't say anything about array having a single data member of type T[N]. In fact, to me it seems to imply that it should behave like an aggregate of `N` elements, not an aggregate containing a single element (see §23.3.2.1). – juanchopanza Jul 09 '12 at 18:15
  • @juanchopanza: The specification says that it is an aggregate and that it "can be initialized with the syntax `array a = { initializer-list };` where _initializer-list_ is a comma-separated list of up to `N` elements whose types are convertible to `T`." An implementation with a single data member of type `T[N]` (as demonstrated in the exposition in the specification), would meet this requirement, but would apparently not be initializable via the direct list initializer. – James McNellis Jul 09 '12 at 18:17
33

Because std::vector offers a constructor that takes in a std::initializer_list<T>, while std::array has no constructors and the {1, 2, 3, 4} braced init-list is in fact not interpreted as a std::initializer_list, but aggregate initialization for the inner C-style array of std::array (that's where the second set of braces comes from: One for std::array, one for the inner C-style member array).

Xeo
  • 129,499
  • 52
  • 291
  • 397