41

Possible Duplicate:
How do I initialize a member array with an initializer_list?

You can construct an std::array just fine with an initializer list:

std::array<int, 3> a = {1, 2, 3};  // works fine

However, when I try to construct it from an std::initializer_list as a data member or base object in a class, it doesn't work:

#include <array>
#include <initializer_list>

template <typename T, std::size_t size, typename EnumT>
struct enum_addressable_array : public std::array<T, size>
{
    typedef std::array<T, size> base_t;
    typedef typename base_t::reference reference;
    typedef typename base_t::const_reference const_reference;
    typedef typename base_t::size_type size_type;

    enum_addressable_array(std::initializer_list<T> il) : base_t{il} {}

    reference operator[](EnumT n)
    {
        return base_t::operator[](static_cast<size_type>(n));
    }

    const_reference operator[](EnumT n) const
    {
        return base_t::operator[](static_cast<size_type>(n));
    }
};

enum class E {a, b, c};
enum_addressable_array<char, 3, E> ea = {'a', 'b', 'c'};

Errors with gcc 4.6:

test.cpp: In constructor 'enum_addressable_array<T, size, EnumT>::enum_addressable_array(std::initializer_list<T>) [with T = char, unsigned int size = 3u, EnumT = E]':
test.cpp:26:55:   instantiated from here
test.cpp:12:68: error: no matching function for call to 'std::array<char, 3u>::array(<brace-enclosed initializer list>)'
test.cpp:12:68: note: candidates are:
include/c++/4.6.1/array:60:12: note: std::array<char, 3u>::array()
include/c++/4.6.1/array:60:12: note:   candidate expects 0 arguments, 1 provided
include/c++/4.6.1/array:60:12: note: constexpr std::array<char, 3u>::array(const std::array<char, 3u>&)
include/c++/4.6.1/array:60:12: note:   no known conversion for argument 1 from 'std::initializer_list<char>' to 'const std::array<char, 3u>&'
include/c++/4.6.1/array:60:12: note: constexpr std::array<char, 3u>::array(std::array<char, 3u>&&)
include/c++/4.6.1/array:60:12: note:   no known conversion for argument 1 from 'std::initializer_list<char>' to 'std::array<char, 3u>&&'

How can I get it to work so that my wrapper class can be initialized with an initializer-list, as such:

enum_addressable_array<char, 3, E> ea = {'a', 'b', 'c'};
Community
  • 1
  • 1
HighCommander4
  • 50,428
  • 24
  • 122
  • 194

3 Answers3

39

An std::array<> has no constructor that takes an std::initializer_list<> (initializer list constructor) and there is no special language support for what it may mean to pass a std::initializer_list<> to a class' constructors such that that may work. So that fails.

For it to work, your derived class needs to catch all elements and then forward them, a constructor template:

template<typename ...E>
enum_addressable_array(E&&...e) : base_t{{std::forward<E>(e)...}} {}

Note that you need {{...}} in this case because brace elision (omitting braces like in your case) does not work at that place. It's only allowed in declarations of the form T t = { ... }. Because an std::array<> consists of a struct embedding a raw array, that will need two level of braces. Unfortunately, I believe that the exact aggregate structure of std::array<> is unspecified, so you will need to hope that it works on most implementations.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 8
    Clang complains that the input should be `E&& ...e`. (And sometimes, g++ tells me to kill people.) – John McFarlane Mar 15 '13 at 19:12
  • 1
    Wouldn't that overwrite the copy contructor and such? Is possible to restrict it more?, like `template(std::declval()...)...}}> enum_addressable_array(E&&...e) : base_t{{std::forward(e)...}} {}` (I can't make it work) – alfC Jul 08 '13 at 04:50
  • 1
    @alfC A template will never be a better match than the implicitly generated special member functions, as they are exact matches (even when they are implicitly or explicitly declared as deleted). The tricky part, however, is that this will be a better match (and therefore preferred in overload resolution) if you pass in something like `enum_addressable_array &`. – David Stone Nov 24 '13 at 16:04
  • I have a question. You say: `Unfortunately, I believe that the exact aggregate structure of std::array<> is unspecified, so you will need to hope that it works on most implementations.` However, as per http://en.cppreference.com/w/cpp/concept/SequenceContainer#cite_note-1 , `std::array` is required to support braced-init-list assignment. Doesn’t this mean that your solution is required to work as per the C++ standard and you don’t have to rely on implementation details? I’m asking though, and not saying it must work: this level of advancement is on the frontier of my understanding :) –  Nov 14 '16 at 23:13
  • 1
    @gaazkam it is required to support `= { ... }` initialization. But it is not required to support `= {{ .. }}` initialization. Yet, for the member initalizer case, it would only work with the latter. But because it is not required to support the latter, and the former is only required to be supported for the `= { ... }` syntax, in the end you have no portable syntax in the member initializer case. However, I'm not up to date with the latest amendmends to the spec. Perhaps they support this case aswell now. – Johannes Schaub - litb Nov 16 '16 at 09:03
35

Since a std::array is a structure that contains an aggregate (it is not an aggregate itself, and does not have a constructor that takes a std::initializer_list), you can initialize the underlying aggregate inside the structure with an initializer list using a double-braces syntax like so:

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

Note that this is not using std::initializer_list ... this is simply using a C++ initializer list to initialize the publicly accessible array member of std::array.

Jason
  • 31,834
  • 7
  • 59
  • 78
10

An std::array does not have a constructor that takes an std::initializer_list. It's a good thing, because initializer lists can be bigger than the fixed-size of the array.

You can initialize it by testing that the initializer list is not larger than the size of the array and then copying the elements of initializer list to the elems member of std::array with std::copy.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510