7

This C++11 code works fine for me:

#include <iostream>
#include <vector>
#include <array>
using namespace std;

struct str {
    int first, last;
};


vector<str> fields {
    {1,2}, {3,4}, {5,6}
};

int main()
{
    for (str s : fields)
        cout << s.first << " " << s.last << endl;
}

It prints the six expected values.

But if I change vector<str> to array<str,3>, gcc gives me this error: "too many initializers for ‘std::array’".

If I change the initialization of fields thus:

array<str,3> fields {
    str{1,2}, str{3,4}, str{5,6}
};

Things work nicely.

So why do I need str{1,2} when using std::array, but only {1,2} when using std::vector?

2 Answers 2

6

See cppreference's section on aggregate initialization.

The effects of aggregate initialization are:

  • Each array element or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.

  • If the initializer clause is a nested braced-init-list, the corresponding class member is itself an aggregate: aggregate initialization is recursive.

This means that if you had an aggregate inside your struct, such as:

struct str {
    struct asdf
    {
        int first, last;
    } asdf; 
};

asdf would be initialized by the first nested brace-init-list, i.e. { { 1, 2 } }. The reason why you generally need two pairs of braces is because the nested brace-init-list initializes the underlying aggregate in std::array (for example, T a[N]).

However, you can still initialize your array like this:

array<str,3> fields {
    1, 2, 3, 4, 5, 6
};

or:

array<str,3> fields { {
    1, 2, 3, 4, 5, 6
} };

instead.

On the other hand, how you initialize your vector is covered by list initialization. std::vector has a constructor that accepts an std::initializer_list.

The effects of list initialization of an object of type T are:

  • Otherwise, the constructors of T are considered, in two phases:

    • All constructors that take std::initializer_list as the only argument, or as the first argument if the remaining arguments have default values, are examined, and matched by overload resolution against a single argument of type std::initializer_list

Note that you wouldn't be able to initialize your vector ( like this:

vector<str> fields {
    1,2, 3,4, 5,6
};

but:

vector<int> fields {
    1,2, 3,4, 5,6
};

is perfectly fine.

Sign up to request clarification or add additional context in comments.

Comments

4

It's because array initialization is built a bit different from vector.
To initialize an array you need to use two braces.
Because of a syntax feature you can skip it if you initialize just one object.
So the following is ok:

array{1,2,3} -> array{{1,2,3}}

But in your example you initialize multiple objects so the compiler doesn't add additional braces. Using two braces fixes that.

array<str,3> fields {{
    {1,2}, {3,4}, {5,6}
}};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.