54 votes

Utilisation de std::array avec des listes d'initialisation

Sauf erreur de ma part, il devrait être possible de créer un std:array de ces manières :

std::array<std::string, 2> strings = { "a", "b" };
std::array<std::string, 2> strings({ "a", "b" });

Pourtant, en utilisant GCC 4.6.1, je ne parviens pas à les faire fonctionner. Le compilateur dit simplement :

expected primary-expression before ',' token

et pourtant les listes d'initialisation fonctionnent très bien avec std::vector. Alors, qu'en est-il ? Est-ce que je me trompe en pensant que std::array devrait accepter les listes d'initialisation, ou est-ce que l'équipe de la bibliothèque standard C++ de GNU a fait une erreur ?

88voto

Nicol Bolas Points 133791

std::array est drôle. Il se définit en gros comme ceci :

template<typename T, int size>
struct std::array
{
  T a[size];
};

C'est un struct qui contient un tableau. Il n'a pas de constructeur qui prend une liste d'initialisation. Mais std::array est un agrégat selon les règles de C++11, et il peut donc être créé par l'initialisation de l'agrégat. Pour initialiser le tableau par agrégation à l'intérieur de la structure, vous avez besoin d'une deuxième série d'accolades :

std::array<std::string, 2> strings = {{ "a", "b" }};

Notez que la norme suggère que les accolades supplémentaires peuvent être élidées dans ce cas. Il s'agit donc probablement d'un bug de GCC.

11voto

Joseph Points 320

Pour ajouter à la réponse acceptée :

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'}};

tous fonctionnent sur GCC 4.6.3 (Xubuntu 12.01). Cependant,

void f(std::array<char, 2> a)
{
}

//f({'a', 'b'}); //doesn't compile
f({{'a', 'b'}});

le texte ci-dessus nécessite des doubles accolades pour être compilé. La version avec accolades simples entraîne l'erreur suivante :

../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>’

Je ne suis pas sûr de l'aspect de l'inférence/conversion de type qui fait que les choses fonctionnent de cette façon, ou si c'est une bizarrerie de l'implémentation de GCC.

2voto

Pepijn Kramer Points 21

Je suis arrivé un peu tard, mais voici comment je procède en C++17. Je n'utilise pas de liste d'initialisation, juste une liste variadique de valeurs. comme ceci : 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);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X