30

Why does this work:

std::pair<int, int> p = {1,2};
std::vector<std::pair<int, int>> vp = { {1,2}, {3,4} };

But this doesn't?

std::array<int, 2> a = {1,2}; // still ok
std::vector<std::array<int, 2>> va = { {1,2}, {3,4} };

Using g++ 4.5.1 with -std=c++0x, the second line fails with:

error: could not convert ‘{{1, 2}, {3, 4}}’ to ‘std::vector<std::array<int, 2u> >’

Thanks

ildjarn
  • 62,044
  • 9
  • 127
  • 211
andreabedini
  • 1,295
  • 1
  • 13
  • 20

2 Answers2

28

Unfortunately, std::array does not have an initializer list constructor. Indeed, it has no user-defined constructor whatsoever -- this "feature" is a leftover from C++03 where omitting all user-defined constructors was the only way to enable the C-style brace initialization. It is IMHO a defect in the current standard.

So why doesn't built-in brace initialization work in this case? Let's see what std::array looks like under the hood:

template <typename T, int i> struct array {
    T data[i];
    // ...
}

Ok, so doesn't that mean we'd have to use double braces in the initializer (one pair for array, another pair for the data member?

std::array<int, 2> a = { {1, 2} };

C (and consequently C++) has a special rule about brace elision, permitting the omission of the inner braces unless there is an ambiguity. array exploits this feature, allowing us to write

std::array<int, 2> a = { 1, 2 };

So why doesn't the example in the original post work? Because brace elision is only permitted in the context of a C-style aggregate initialization, not if there's anything more complicated involved, such as an user-defined initializer list constructor.

The following should work, though, as ugly as it is:

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

The fact that it does not, at least on gcc 4.5 and gcc 4.6, seems to me to indicate a compiler bug. I'm not completely sure about it, though.

This question is somewhat relevant: How do I initialize a member array with an initializer_list?

Community
  • 1
  • 1
JohannesD
  • 13,802
  • 1
  • 38
  • 30
  • 1
    I rather suspect that `std::array` looks more like `template ` – Chris Lutz May 18 '11 at 10:21
  • Yes, your last example should work. It was a conscious and discussed decision not to support brace elision for anything other than a simple `T t = { ... }` declaration. That's also why `return { ... }` will not work with a `std::array<>` return type. – Johannes Schaub - litb May 18 '11 at 17:22
  • Your last example std::vector> vp = { {{1,2}}, {{3,4}} }; works on gcc 4.7.2 – Ricky65 Apr 01 '13 at 20:25
4

This works:

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

Digging deeper, it seems that std::pair has a constructor that takes an initialiser list, but std::array doesn't:

std::pair<int, int> p ({1,2}) ;  // OK
std::array<int, 2> a ({1,2}) ;   // Invalid

But now I'm out of my depth.

TonyK
  • 16,761
  • 4
  • 37
  • 72
  • you actually made here it for the compiler to infer the types of the elements in the curly brace initializer list. I guess that's where the problem lies, but still do not know whether it should work per the standard or not. – Matthieu M. May 18 '11 at 08:16