35
std::array<std::pair<int, int>, 2> ids = { { 0, 1 }, { 1, 2 } };

VS2013 error:

error C2440: 'initializing' : cannot convert from 'int' to 'std::pair' No constructor could take the source type, or constructor overload resolution was ambiguous`

What am I doing wrong?

Jamal
  • 763
  • 7
  • 22
  • 32
Neil Kirk
  • 21,327
  • 9
  • 53
  • 91

2 Answers2

39

Add another pair of braces.

std::array<std::pair<int, int>, 2> ids = { { { 0, 1 }, { 1, 2 } } };

std::array<T, N> is an aggregate class containing a member of type T[N]. Usually, you can initialise that the same way you would a plain T[N] array, but when you're dealing with a non-aggregate element type, you may need to be more explicit.

  • This seems to explain everything, until you look at Joachim's answer using `std::make_pair`. How does _that_ work, without the extra pair of enclosing braces? – TonyK Dec 27 '14 at 17:24
  • @TonyK In general, braces can be omitted. `int a[2][2] = { 0, 1, 2, 3 };` is perfectly valid. But when you're dealing with classes with user-provided constructors, things get a bit tricky: `{ 0, 1 }` could be an attempt to initialise the first element, or the first sub-element. The ambiguity is resolved in favour of the first element. On the other hand, the result of `make_pair` can only be used to initialise the first sub-element. –  Dec 27 '14 at 17:35
  • The only thing the standard guarantees to work is "`array a = { initializer-list }; ` where *initializer-list* is a comma-separated list of up to `N` elements whose types are convertible to `T`". The extra pair of braces will probably work on all current implementations, but isn't guaranteed. – T.C. Dec 27 '14 at 17:44
  • @T.C. Not yet, anyway. There are several relevant open issues about arrays. One is that `array` should be layout-compatible with `T[N]`. Another is that the member `T elems[N];` in the standard's description of `std::array`, commented as "exposition only", isn't intended to work like how [objects.within.classes]p2 says such members should work. For practical purposes, this answer is correct. A strict reading of the standard doesn't support this answer, but does support the position that the standard is simply wrong and no meaningful conclusions can be drawn from it just yet. :) –  Dec 27 '14 at 17:59
  • 1
    The suggestion here does not work if the array contains three pairs: `std::array, 3> ids = { { { 0, 1 }, { 1, 2 }, { 2, 3 } } };` fails to compile. However, leaving away the `=` helps: `std::array, 3> ids { { { 0, 1 }, { 1, 2 }, { 2, 3 } } };` compiles without an error. It took me quite a while to figure that out. Maybe the answer should be edited so that future visitors will be warned? Plus, bonus question from myself: Why is this so? – Tim Feb 21 '18 at 20:19
  • @Tim Exactly what you say doesn't compile, does compile just fine. Double-checked on https://gcc.godbolt.org/ with current and older releases of GCC, clang, icc and MSVC. –  Feb 21 '18 at 20:44
  • @hvd Oops, this is weird. Sorry to “wake you up” on this. I definitely observed the above, with a direct comparison of the two options, in Xcode. Now I cannot reproduce it. Looks like one of the cases when a restart of Xcode makes compilation errors go away? Strange it happened in this case, though. – Tim Feb 22 '18 at 08:07
29

std::array is an aggregate. It has only one data member - an array of the specified type of the std::array specialization. According to the C++ Standard. (8.5.1 Aggregates)

2 When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order

So this record

std::array<std::pair<int, int>, 2> ids = { { 0, 1 }, { 1, 2 } };

has more initializers then there are data members in std::array.

The data member of std::array is in turn an aggregate. You have to provide for it an initializer list.

So the record will look like

std::array<std::pair<int, int>, 2> ids = { { { 0, 1 }, { 1, 2 } } };

For it would be more clear you can imagine the initialization the following way

std::array<std::pair<int, int>, 2> ids = { /* an initializer for data member of the array */ };

As the data member is aggregate then you have to write

std::array<std::pair<int, int>, 2> ids = { { /* initializers for the aggregate data member*/ } };

And at last

std::array<std::pair<int, int>, 2> ids = { { { 0, 1 }, { 1, 2 } } };
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 2
    If you are going by the standard, then there's no guarantee that `std::array` has only one data member. – T.C. Dec 27 '14 at 17:40
  • @T.C. Without this information you can not correctly initialize the array. At least for exposition the standard includes in the array definition the following data member T elems[N]; – Vlad from Moscow Dec 27 '14 at 17:45
  • The standard guarantees that `array a = { initializer-list };` will work if the initializer-list has up to N elements each of a type convertible to `T`. There's no guarantee that the built-in array (if one is used - I do not know if it is possible to comply with all the requirements without using one) is the only data member. – T.C. Dec 27 '14 at 17:50
  • @T.C. I also do not know what can substitute the array. It seems it would be better if there would be written explicitly that the internal representation is an array. – Vlad from Moscow Dec 27 '14 at 17:56
  • For the passing-by-reader, this stuff is now in 9.4.2 – Enlico Aug 30 '22 at 08:37