141 votes

Comment faire pour émuler l’initialisation du tableau C « int arr [] = {e1, e2, e3,...} » comportement avec std::array ?

(Remarque: Cette question est à propos de ne pas avoir à spécifier le nombre d'éléments et permettent encore les types imbriqués être initialisée directement.)
Cette question traite de l'utilisation à gauche pour un C tableau comme int arr[20];. Sur sa réponse, @James Kanze montre l'un des derniers bastions de C des tableaux, il est unique initialisation caractéristiques:

int arr[] = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 };

Nous n'avons pas à spécifier le nombre d'éléments, hourra! Maintenant parcourir avec le C++11 fonctions, std::begin et std::end de <iterator> (ou vos propres variantes) et vous ne devez même pas penser à sa taille.

Maintenant, il y a de tout (éventuellement TMP) des moyens de réaliser la même chose avec std::array? L'utilisation de macros a permis de le rendre plus agréable. :)

??? std_array = { "here", "be", "elements" };

Edit: version Intermédiaire, compilées à partir des réponses très diverses, ressemble à ceci:

#include <array>
#include <utility>

template<class T, class... Tail, class Elem = typename std::decay<T>::type>
std::array<Elem,1+sizeof...(Tail)> make_array(T&& head, Tail&&... values)
{
  return { std::forward<T>(head), std::forward<Tail>(values)... };
}

// in code
auto std_array = make_array(1,2,3,4,5);

Et emploie toutes sortes de cool C++11 trucs:

  • Variadic Templates
  • sizeof...
  • références rvalue
  • le transfert parfait
  • std::array, bien sûr
  • l'initialisation uniforme
  • en omettant le type de retour avec l'initialisation uniforme
  • l'inférence de type (auto)

Et un exemple peut être trouvé ici.

Cependant, comme @Johannes points dans le commentaire sur @Xaade réponse, vous ne pouvez pas initialiser les types imbriqués avec une telle fonction. Exemple:

struct A{ int a; int b; };

// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
??? std_array = { {1,2}, {3,4} };

Aussi, le nombre de initialiseurs est limité au nombre de fonction et les arguments de modèle pris en charge par la mise en œuvre.

65voto

Pavel Minaev Points 60647

Mieux que je peux penser est :

Cependant, cette technique nécessite le compilateur pour faire NRVO et également ignorer la copie de la valeur retournée (qui est également autorisée mais pas exigée). Dans la pratique, je m’attends tout compilateur C++ pour pouvoir optimiser que tel qu’il est aussi rapide que l’initialisation directe.

38voto

Puppy Points 90818

Je m’attends un simple `` .

20voto

Kerrek SB Points 194696

En combinant quelques idées de posts précédents, voici une solution qui fonctionne même pour les constructions imbriquées (testé dans GCC4.6):

template <typename T, typename ...Args>
std::array<T, sizeof...(Args) + 1> make_array(T && t, Args &&... args)
{
  static_assert(all_same<T, Args...>::value, "make_array() requires all arguments to be of the same type."); // edited in
  return std::array<T, sizeof...(Args) + 1>{ std::forward<T>(t), std::forward<Args>(args)...};
}

Étrangement, ne peuvent rendre la valeur de retour d'une référence rvalue, cela ne fonctionnerait pas pour les constructions imbriquées. De toute façon, voici un test:

auto q = make_array(make_array(make_array(std::string("Cat1"), std::string("Dog1")), make_array(std::string("Mouse1"), std::string("Rat1"))),
                    make_array(make_array(std::string("Cat2"), std::string("Dog2")), make_array(std::string("Mouse2"), std::string("Rat2"))),
                    make_array(make_array(std::string("Cat3"), std::string("Dog3")), make_array(std::string("Mouse3"), std::string("Rat3"))),
                    make_array(make_array(std::string("Cat4"), std::string("Dog4")), make_array(std::string("Mouse4"), std::string("Rat4")))
                    );

std::cout << q << std::endl;
// produces: [[[Cat1, Dog1], [Mouse1, Rat1]], [[Cat2, Dog2], [Mouse2, Rat2]], [[Cat3, Dog3], [Mouse3, Rat3]], [[Cat4, Dog4], [Mouse4, Rat4]]]

(Pour la dernière sortie que je suis avec mon imprimeur.)


En fait, permettez-nous à améliorer le type de sécurité de cette construction. Nous avons certainement besoin de tous les types d'être le même. Une façon est d'ajouter un statique affirmation, que j'ai édité dans ci-dessus. L'autre façon est d'activer make_array quand les types sont les mêmes, comme suit:

template <typename T, typename ...Args>
typename std::enable_if<all_same<T, Args...>::value, std::array<T, sizeof...(Args) + 1>>::type
make_array(T && t, Args &&... args)
{
  return std::array<T, sizeof...(Args) + 1> { std::forward<T>(t), std::forward<Args>(args)...};
}

De toute façon, vous aurez besoin de les variadic all_same<Args...> type de trait. Ici, il est, de généraliser à partir d' std::is_same<S, T> (à noter que la décomposition est important de permettre le mélange de T, T&, T const & etc.):

template <typename ...Args> struct all_same { static const bool value = false; };
template <typename S, typename T, typename ...Args> struct all_same<S, T, Args...>
{
  static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value && all_same<T, Args...>::value;
};
template <typename S, typename T> struct all_same<S, T>
{
  static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value;
};
template <typename T> struct all_same<T> { static const bool value = true; };

Notez que make_array() retours par copie-de-temporaire, dont le compilateur (avec suffisamment d'optimisation de drapeaux!) est autorisé à traiter comme une rvalue ou autrement optimiser loin, et std::array est un type de regroupement, de sorte que le compilateur est libre de choisir la meilleure possible de la méthode de construction.

Enfin, notez que vous ne pouvez pas éviter le copier/déplacer de construction lors de l' make_array met en place l'initialiseur. Donc, std::array<Foo,2> x{Foo(1), Foo(2)}; n'a pas de copier/déplacer, mais auto x = make_array(Foo(1), Foo(2)); a deux copier/se déplace comme les arguments sont transmis make_array. Je ne pense pas que vous pouvez l'améliorer, parce que vous ne pouvez pas passer un variadic liste d'initialiseur lexicalement à l'aide et d' en déduire le type et la taille -- si le préprocesseur avait un sizeof... fonction pour variadic arguments, peut-être cela pourrait être fait, mais pas dans le langage de base.

10voto

Ricky65 Points 538

Le fait que std::array ne peut pas être initialisé comme un C-tableau est mauvais. J'ai fait un post à ce sujet sur comp.lang.c++.animé il y a quelques mois où un make_array solution a été offert comme ici.

Lien pour ceux que ça intéresse: http://groups.google.com/group/comp.lang.c++.modéré/browse_thread/thread/f1a5a1451b003bec

Des fonctions telles que make_array sont tous bien, mais vous ne devriez pas avoir à utiliser une fonction compliquée pour initialiser quelque chose d'aussi fondamental que celui d'un tableau. Il est un autre domaine qui donne à la C++ d'une mauvaise réputation. Si une langue ne peut pas obtenir de l'initialisation d'un tableau à droite, puis qui montre une situation désespérée de l'omi.

Des exemples comme ci-dessous devrait fonctionner! Pas plus de bêtises s'il vous plaît!

std::array <int> arr = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 }; //automatically deduces its size from the initializer list :)

6voto

Richard Points 2089

C ++11 soutiendra cette manière d’initialisation pour les conteneurs de MST (la plupart ?).

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