14

Can I initialize an array using the std::initializer_list object instead of brace-enclosed initializer?

As known, we can do this: http://en.cppreference.com/w/cpp/language/aggregate_initialization

unsigned char b[5]{"abc"};
// equivalent to unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'};

int ar[] = {1,2,3};
std::array<int, 3> std_ar2{ {1,2,3} };    // std::array is an aggregate
std::array<int, 3> std_ar1 = {1, 2, 3};

But I can't initialize an array by std::initializer_list il;:

http://ideone.com/f6aflX

#include <iostream>
#include <initializer_list>
#include <array>

int main() {

    int arr1[] =  { 1, 2, 3 };  // OK
    std::array<int, 3> arr2 =  { 1, 2, 3 }; // OK

    std::initializer_list<int> il = { 1, 2, 3 };
    constexpr std::initializer_list<int> il_constexpr = { 1, 2, 3 };

    //int arr3[] = il;  // error
    //int arr4[] = il_constexpr;    // error

    //std::array<int, 3> arr5 =  il;    // error
    //std::array<int, 3> arr6 =  il_constexpr;  // error

    return 0;
}

But how can I use std::initializer_list il; to initialize an array?

2
  • 5
    Repoened.. the "duped" was a general question about initialization of a member array with { .. }. This one asks to initialize a std:: type (no changes allowed to it!) using std::initializer_lists. They are vastly different questions. Commented Aug 13, 2016 at 11:17
  • @Alex Can't you use copy from algorithm (example)? Commented Aug 13, 2016 at 12:19

3 Answers 3

10

Other answered correctly said this is not possible upfront. But with little helpers, you can get pretty close

template<typename T, std::size_T N, std::size_t ...Ns>
std::array<T, N> make_array_impl(
    std::initializer_list<T> t,
    std::index_sequence<Ns...>) 
{
    return std::array<T, N>{ *(t.begin() + Ns) ... };
}

template<typename T, std::size_t N>
std::array<T, N> make_array(std::initializer_list<T> t) {
    if(N > t.size())
       throw std::out_of_range("that's crazy!");
    return make_array_impl<T, N>(t, std::make_index_sequence<N>());
}

If you are open to more work arounds, you can put this into a class to catch statically-known length violations for the cases where you pass a braced init list. But be warned that most people who read this code will head-desk

template<typename T, std::size_t N>
struct ArrayInitializer {
    template<typename U> struct id { using type = U; };
    std::array<T, N> t;

    template<typename U = std::initializer_list<T>>
    ArrayInitializer(typename id<U>::type z) 
        :ArrayInitializer(z, std::make_index_sequence<N>())
    { 
        if(N > z.size())
            throw std::out_of_range("that's crazy!");
    }

    template<typename ...U>
    ArrayInitializer(U &&... u)
       :t{ std::forward<U>(u)... }
    { }

private:
    template<std::size_t ...Ns>
    ArrayInitializer(std::initializer_list<T>& t,
                     std::index_sequence<Ns...>)
       :t{ *(t.begin() + Ns) ... }
    { }
};

template<typename T, std::size_t N>
std::array<T, N> f(ArrayInitializer<T, N> ai) {
    return std::move(ai.t);
}

int main() {
   f<int, 5>({1, 2, 3, 4, 5});  // OK 
   f<int, 5>({1, 2, 3, 4, 5, 6});  // "too many initializers for array<int, 5>"

   std::initializer_list<int> il{1, 2, 3, 4, 5};
   f<int, 5>(il); // ok
}

Note that both the non-static case at the top of the answer and the "head-desk" case do only check whether you provide too few initializing elements, and errors out then, for the initializer_list case. If you provide too many for the initializer_list case, the trailing elements are just ignored.

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

2 Comments

Thank you! Yes, both examples are usable: ideone.com/s0ciUW and ideone.com/fbnM6W
I suppose that with C++17 constexpr we can have more graceful error handling for statically-known length violations; initializer_list.size() is constexpr after all.
7

As far I know, no: you can't initialize a std::array with a std::initializer_list.

The problem is that std::array is intended as a lightweight replacement (a wrapper) for the classic C-style array. So light that is without constructors, so only implicit constructor can be used.

The construction with aggregate initialization (via implicit constructor) is possible because it's possible for the C-style array.

But std::initializer_list is a class, more complicated than an aggregate inizialization.

You can initialize, by example, a std::vector with a std::initializer_list but only because there is an explicit constructor, for std::vector, that receive a std::initializer_list. But std::vector is a heavier class.

The only solution that I see is a 2 step way: (1) construction and (2) copy of the std::initializer_list values. Something like

std::array<int, 3> arr5;

auto  ui = 0U;
auto  cit = il.cbegin();

while ( (ui < arr5.size()) && (cit != il.cend()) )
   arr5[ui++] = *cit++;

p.s.: sorry for my bad English.

2 Comments

Bad English? You write better than most of my colleagues. You don't need comments like that in Stack overflow answers or questions. The community will edit anything that's not understandable.
@AndonM.Coleman - thank; I'll try to ignore my language insecurity.
3

The problem with std::array is that it is required to be an aggregate type, hence it does not have constructors.

Hence only aggregate initialization or trivial copy are possible. std::initializer_list is a class other than std::array, so a (missing) implicit conversion is required.

See http://en.cppreference.com/w/cpp/language/aggregate_initialization and http://en.cppreference.com/w/cpp/container/array for reference.

2 Comments

@chris: Sorry, not POD, but Aggregate type. See en.cppreference.com/w/cpp/language/aggregate_initialization and en.cppreference.com/w/cpp/container/array. Just fixed the text

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.