Using std::array with initialization lists
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);
Chris_F
Updated on August 13, 2021Comments
-
Chris_F almost 3 years
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?
-
Daniel over 12 yearsDid the standard committee do this on purpose?
-
Paul Manta over 12 yearsWhy doesn't the standard just require that
std::array
have a constructor that works with initializer lists? -
Nicol Bolas over 12 years@PaulManta: Because then it wouldn't qualify for aggregate initialization. Aggregate initialization can be folded in at compile-time, depending on the type of the array elements (std::string doesn't qualify). Initializer list initialization must be a runtime function call, regardless of the type of the array elements.
-
Nicol Bolas over 12 years@Dani: Not necessarily "on purpose". It's simply a necessary outgrowth of how things have to work.
std::array
is intended to be a compile-time type, so it needs to work with aggregate initialization. -
Chris_F over 12 yearsOk, so I found this on Wikipedia en.wikipedia.org/wiki/…. Note how it says Note that for standard conforming compilers it is possible to use fewer braces (according to 8.5.1 (11) of the Standard). So it seems the GNU team has goofed a bit and SHOULD be able to initialize a std::array as std::array<std::string, 2> strings = { "a", "b" };