Using std::array with initialization lists
Unless I am mistaken, it should be possible to create a std:array in these ways:
std::array<std::string, 2> strings = { "a", "b" };
std::array<std::string, 2> strings({ "a", "b" });
And yet, using GCC 4.6.1 I am unable to get any of these to work. The compiler simply says:
expected primary-expression before ',' token
and yet initialization lists work just fine with std::vector. So which is it? Am I mistaken to think std::array should accept initialization lists, or has the GNU Standard C++ Library team goofed?
Solution 1:
std::array
is funny. It is defined basically like this:
template<typename T, int size>
struct std::array
{
T a[size];
};
It is a struct which contains an array. It does not have a constructor that takes an initializer list. But std::array
is an aggregate by the rules of C++11, and therefore it can be created by aggregate initialization. To aggregate initialize the array inside the struct, you need a second set of curly braces:
std::array<std::string, 2> strings = {{ "a", "b" }};
Note that the standard does suggest that the extra braces can be elided in this case. So it likely is a GCC bug.
Solution 2:
To add to the accepted answer:
std::array<char, 2> a1{'a', 'b'};
std::array<char, 2> a2 = {'a', 'b'};
std::array<char, 2> a3{{'a', 'b'}};
std::array<char, 2> a4 = {{'a', 'b'}};
all work on GCC 4.6.3 (Xubuntu 12.01). However,
void f(std::array<char, 2> a)
{
}
//f({'a', 'b'}); //doesn't compile
f({{'a', 'b'}});
the above requires double braces to compile. The version with single braces results in the following error:
../src/main.cc: In function ‘int main(int, char**)’:
../src/main.cc:23:17: error: could not convert ‘{'a', 'b'}’ from ‘<brace-enclosed initializer list>’ to ‘std::array<char, 2ul>’
I'm not sure what aspect of type inference/conversion makes things work this way, or if this is a quirk of GCC's implementation.
Solution 3:
A bit late to the game but this is how I do it in C++17. Not using an initializer list just a variadic list of values. like this : auto ar2 = create_array(1, 2, 3, 4);
#include <array>
#include <type_traits>
namespace traits
{
template<typename T, typename... Ts>
struct array_type
{
using type = T;
};
template<typename T, typename... Ts>
static constexpr bool are_same_type()
{
return std::conjunction_v<std::is_same<T, Ts>...>;
}
}
template<typename... T>
constexpr auto create_array(const T&&... values)
{
using array_type = typename traits::array_type<T...>::type;
static_assert(sizeof...(T) > 0, "an array must have at least one element");
static_assert(traits::are_same_type<T...>(), "all elements must have same type");
return std::array<array_type, sizeof...(T)>{ values... };
}
template<typename T, typename... Ts>
constexpr auto create_array_t(const Ts&&... values)
{
using array_type = T;
static_assert(sizeof...(Ts) > 0, "an array must have at least one element");
static_assert(traits::are_same_type<Ts...>(), "all elements must have same type");
return std::array<array_type, sizeof...(Ts)>{ static_cast<T>(values)... };
}
// to create a std::array of specific type
auto ar = create_array_t<std::uint8_t>(1u, 2u, 3u, 4u);
static_assert(ar.size() == 4);
// to create an array and let the compiler deduce its type
auto ar2 = create_array(1, 2, 3, 4);
static_assert(ar2.size() == 4);