Using std::array with initialization lists

40,382

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);
Share:
40,382
Chris_F
Author by

Chris_F

Updated on August 13, 2021

Comments

  • Chris_F
    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
    Daniel over 12 years
    Did the standard committee do this on purpose?
  • Paul Manta
    Paul Manta over 12 years
    Why doesn't the standard just require that std::array have a constructor that works with initializer lists?
  • Nicol Bolas
    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
    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
    Chris_F over 12 years
    Ok, 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" };