What are pros and cons of std::initializer_list and c array []?
Plain and simple: initializer_list
isn't a container. It's an immutable view onto externally allocated elements. It's utterly unsuitable for any scenario a container would be useful in—consider the needless indirection (no resizability), the immutability, the idiomacy of its name. On top of that, it has no proper interface.
A situation where both seem adequate is a constructor parameter for a sequence. If the length is fixed (or template-parametrized), then int const (&arr)[N]
is possible, although initializer_list
is far simpler and more flexible. After all, that's what it was designed and intended for..
As written in comments, it's a broad argument.
Anyway, I point your attention regarding a point.
In first case
X x1[] {{5, 6.3}};
the number of element of x1
is part of the x1
type.
So you have that
X x1[] {{5, 6.3}};
X x2[] {{5, 6.3}, {7, 8.1}};
static_assert( false == std::is_same<decltype(x1), decltype(x2)>::value );
Using an initializer list
std::initializer_list<X> x3 {{5, 6.3}};
std::initializer_list<X> x4 {{5, 6.3}, {7, 8.1}};
static_assert( true == std::is_same<decltype(x3), decltype(x4)>::value );
the type remain the same changing the number of elements.
According to your needs this can be an advantage for first or second solution.
The fact that the number of elements is part of the type for C-style arrays can be a little advantage in meta programming.
Suppose you want a function that return the sum of the i
values of the arrays, with C-style array you can write
template <std::size_t N, std::size_t ... Is>
constexpr auto sum_i_helper (X const (&xArr)[N], std::index_sequence<Is...>)
{ return (... + xArr[Is].i); }
template <std::size_t N>
constexpr auto sum_i (X const (&xArr)[N])
{ return sum_i_helper(xArr, std::make_index_sequence<N>{}); }
and this function compile also when the argument of sum_i()
is a not-constexpr value.
If you wan to write something similar with std::initializer_list
is a little more complicated because the size()
of the list isn't necessarily a compile-time known value so or you pass it as template parameter (but the function doesn't works with run-time lists) or you use size()
inside the function, but you can't use it to initialize a std::index_sequence
.
Anyway, with initializer list, you can use the good old for()
cycle
constexpr auto sum_i (std::initializer_list<X> const lx)
{
int ret { 0 };
for ( auto const & x : lx )
ret += x.i;
return ret;
}
and the function can compute compile time when lx
is a constexpr
value.
Also of concern is if you will be able to use/convert the one type to the other type - eg. when constructing standard containers ?
Convert an array to an initializer list it's easy an works with both compile-time and run-time known value
template <std::size_t N, std::size_t ... Is>
constexpr auto convertX_h (X const (&xArr)[N], std::index_sequence<Is...>)
{ return std::initializer_list<X>{ xArr[Is]... }; }
template <std::size_t N>
constexpr auto convertX (X const (&xArr)[N])
{ return convertX_h(xArr, std::make_index_sequence<N>{}); }
// ....
X x1[] {{5, 6.3}};
std::initializer_list<X> x5 = convertX(x1);
Converting an initializer list to a C-style array is more difficult because the type of the array depends from the number of elements so you need to know compile-time the number of elements in the initializer list, because you can't randomly access to an initializer list and, worse, because you can't write a function that return a C-style array.
I can imagine a solution as follows that convert an initializer list to a std::array
(off topic suggestion: use std::array
, instead of a C-style array, when possible)
template <std::size_t N>
constexpr auto convertX (std::initializer_list<X> const lx)
{
std::array<X, N> ret;
std::size_t i { 0u };
for ( auto const & x : lx )
ret[i++] = x;
return ret;
}
// ...
constexpr std::initializer_list<X> x4 {{5, 6.3}, {7, 8.1}};
auto x6 = convertX<x4.size()>(x4);
but x6
now is a std::array<X, 2>
, not a X[2]
, and x4
must be a constexpr
value.