4

I want to initialize a vector or an array with a list of objects. It works for vectors, but not for arrays:

struct Widget
{
    string name;
    vector<int> list;
};

struct Object
{
    string name;
    vector<int> list;
    Object(string _name, vector<int> _list) : name(_name), list(_list) { }
};

int main()
{
    const vector<Widget> vw = {
        {"vw1", {1,2,3}},
        {"vw2", {1,2,3}} };
    const array<Widget,2> aw = {
        {"aw1", {1,2,3}},
        {"aw2", {1,2,3}} };
    const vector<Object> vo = {
        {"vo1", {1,2,3}},
        {"vo2", {1,2,3}} };
    const array<Object,2> ao = {
        {"ao1", {1,2,3}},
        {"ao2", {1,2,3}} };
    return 0;
}

The error from clang:

widget.cpp:36:9: error: excess elements in struct initializer
        {"aw2", {1,2,3}} };
        ^~~~~~~~~~~~~~~~
widget.cpp:41:10: error: no viable conversion from 'const char [4]' to 'Object'
        {"ao1", {1,2,3}},
         ^~~~~
widget.cpp:41:17: error: no matching constructor for initialization of 'Object'
        {"ao1", {1,2,3}},
                ^~~~~~~

What is the difference between vector and array, which prevents the array type from supporting this syntax?

Jan Müller
  • 413
  • 3
  • 18
  • I'm not 100% certain, but I believe this is a weird interaction between aggregate initialisation and brace-enclosed initialiser lists. It can be solved as described in the answers below, but I'm not sure what the specific cause of the issue is. Closer examination of the error messages indicates that the array is "absorbing" an additional brace, anyways, for lack of a better term; note how it thinks `"ao1` and `{1,2,3}` are two different `Object`s, instead of part of the same `Object`'s initialiser list. – Justin Time - Reinstate Monica Feb 06 '17 at 22:14

2 Answers2

4

Here is working solution - you need double braces for arrays.

int main()
{
    const vector<Widget> vw = {
        {"vw1", {1,2,3}},
        {"vw2", {1,2,3}} };
    const array<Widget,2> aw = {{
        {"aw1", {1,2,3}},
        {"aw2", {1,2,3}} }};
    const vector<Object> vo = {
        {"vo1", {1,2,3}},
        {"vo2", {1,2,3}} };
    const array<Object,2> ao = {{
        {"ao1", {1,2,3}},
        {"ao2", {1,2,3}} }};
    return 0;
}

Why?

http://en.cppreference.com/w/cpp/container/array

std::array is a container that encapsulates fixed size arrays. This container is an aggregate type with the same semantics as a struct holding a C-style array T[N] as its only non-static data member. Unlike a C-style array, it doesn't decay to T* automatically. As an aggregate type, it can be initialized with aggregate-initialization given at most N initializers that are convertible to T: std::array a = {1,2,3};.

Theoretical implementation (it is more complicated in reality)

template <typename T, size_t size> 
struct array  
{
  T data[size]; 
}

So first brace is to aggregate-initializate array object on its own, second brace to aggregate-initializate internal "legacy C style" array.

Anty
  • 1,486
  • 1
  • 9
  • 12
  • That's interesting. These are a lot of braces... But makes sense what's written. Also the linked question/answer helps. Thanks! – Jan Müller Feb 07 '17 at 07:05
0

Adding another pair of braces to your examples will enable them to work correctly:

int main()
{
    const std::array<Widget,2> aw = {
        {
            {"aw1", {1, 2, 3}},
            {"aw2", {1, 2, 3}}
        }
    };

    const std::array<Object, 2> ao = {
        {
            {"ao1", {1, 2, 3} },
            {"ao2", {1, 2, 3} }
        }
    };

    cout << aw[0].name << " " << aw[1].list[1] << endl;
    cout << ao[0].name << " " << ao[1].list[1] << endl;

    return 0;
}

Will give output:

aw1 2
ao1 2

The extra pair of braces is needed as std::array has no constructor itself and is simply using aggregate initialisation rather than list initialisation.

In terms of implementation, a std::array consists of one internal item, that being the real underlying array of N elements. Thus we need the extra braces so we are initialising the std::array with one element (that being an array of, in your examples, two elements)

donkopotamus
  • 22,114
  • 2
  • 48
  • 60