How to construct std::array object with initializer list?

27,242

Solution 1

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.

Solution 2

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.

Solution 3

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.

Share:
27,242
HighCommander4
Author by

HighCommander4

Updated on July 09, 2022

Comments

  • HighCommander4
    HighCommander4 almost 2 years

    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'};
    
  • John McFarlane
    John McFarlane about 11 years
    Clang complains that the input should be E&& ...e. (And sometimes, g++ tells me to kill people.)
  • alfC
    alfC almost 11 years
    Wouldn't that overwrite the copy contructor and such? Is possible to restrict it more?, like template<typename ...E, class = decltype(base_t{{std::forward<Args__>(std::declval<Args__>()‌​...)...}}> enum_addressable_array(E&&...e) : base_t{{std::forward<E>(e)...}} {} (I can't make it work)
  • David Stone
    David Stone over 10 years
    @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 &.
  • Admin
    Admin over 7 years
    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 en.cppreference.com/w/cpp/concept/SequenceContainer#cite_not‌​e-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 :)
  • Johannes Schaub - litb
    Johannes Schaub - litb over 7 years
    @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.