2016-04-03 2 views
1

J'ai une classe de liste dans laquelle la variable de taille est un membre const. Ceci est utile pour moi car il impose l'exigence que la taille de la liste peut varier d'une exécution à l'autre, mais ne peut pas varier au cours d'une exécution individuelle.Construire std :: array en remplissant un élément

Je voudrais créer une collection de ces listes. Le nombre de listes dans la collection est une variable de modèle, donc je voudrais utiliser un std::array ... ie, je voudrais un tableau de listes, où la taille de la matrice est un paramètre de modèle et la taille de chaque la liste est une const spécifiée à la construction

Malheureusement:

  • La liste taille const n'a pas de constructeur par défaut (sa taille doit être spécifiée!), donc je dois fournir un argument de constructeur à chaque élément de la liste. Je ne peux pas créer le tableau, puis définissez les éléments
  • Parce que ma taille de la liste est une variable de modèle, je ne peux pas utiliser une liste standard initialiseur - les éléments de nombre requis varie

Je reconnais qu'il y des alternatives:

  • je pouvais utiliser un std::vector et juste push_back les éléments un par un jusqu'à ce que la taille du vecteur est égal à mon paramètre de modèle, mais cela semble inélégante, car il ne serait pas respecter naturellement la condition que la La taille du vecteur résultant ne doit pas être modifiée une fois qu'il est entièrement rempli.
  • Je pourrais inverser l'ordre d'index, et avoir une liste const-size de std::arrays. Cela ne correspond pas bien avec le reste de mon code, cependant; Je voudrais pouvoir passer une liste individuelle const-size du tableau au code client
  • Je pourrais créer un constructeur par défaut pour la classe de liste const size, créer le tableau, puis utiliser le placement new pour remplacer les éléments du tableau un par un. Cela semble que cela pourrait avoir des effets secondaires néfastes (qu'est-ce que le constructeur par défaut de la liste de const taille faire? Et s'il est appelé par hasard ailleurs? Qu'est-ce qui se passe quand mon successeur n'a aucune idée de ce que je l'ai fait?)

Étant donné qu'aucun d'entre eux sont tout à fait idéal, je pense que ce serait génial s'il y avait un constructeur de tableau (ou la fonction d'aide) qui prendrait, comme arguments:

  1. le nombre d'éléments dans le tableau de T
  2. Un seul objet T

... et renvoie un std::array<T> où chaque T a été copié à partir de l'argument 2.

Une telle chose existe-t-elle?

+0

"La taille de chaque liste est un const spécifié lors de la construction" - J'espère sincèrement que vous voulez dire aussi à * compile-time *, n'est-ce pas? Sinon, vous pouvez jeter l'idée d'utiliser 'std :: array ' dès le début. – WhozCraig

+0

La taille des listes n'est pas une constante de temps de compilation. – user1476176

Répondre

2

Ok. Modèle de magie. Comme std::array est un type global, il peut être initialisé à l'aide d'initialisation globale:

std::array<T, 5> arr = { one, two, three, four, five }; 

L'idée est one, ..., five sont cinq copies d'un objet construit de T (liste dans votre cas) de type, en utilisant l'entrée de l'utilisateur en tant que paramètre. Alors, laisse jouer. Ne pas rire ou pleurer après avoir lu ceci:

L'idée est de prendre un objet de type T, et de le reproduire cinq fois:

{ T(param), .... }; // Five repetitions. 

donc, permet de créer une fonction qui renvoie le tableau déjà initialisé :

std::array<A, 5> arr = create_array_by_copy<5>(A(10)); 

cette fonction retourne un array<A, 5> avec 5 copies de cet objet temporaire.

Pour cela, nous allons utiliser un struct auxiliaire qui va créer un pack de paramètres avec une longueur de 5 (paramétrés comme s):

template<std::size_t s, class... voids_t> 
struct sized_pack : public sized_pack<s - 1, voids_t..., void> 
{}; 

template<class... voids_t> 
struct sized_pack<0, voids_t...> 
{}; 

Cela va créer un pack de paramètres, appelé voids_t, qui est juste une liste de svoid s. Et maintenant, le noyau de l'astuce:

template<std::size_t s, class T, class... pack_t> 
std::array<T, s> 
create_array_by_copy_helper(sized_pack<0, pack_t...> const&, 
          T const& o) 
{ return { (pack_t(), o)... }; } 

template<std::size_t s, class T> 
std::array<T, s> create_array_by_copy(T const& o) 
{ 
    return create_array_by_copy_helper<s>(sized_pack<s>(), o); 
} 

C'est compliqué. Je sais ... comme nous avons passé un objet de type sized_pack<s> à la fonction d'assistance, cet objet temporaire va instancier une hiérarchie de sized_pack dont la dernière classe de base sera un objet de type sized_pack<0, void, void, void, void, void>.

La fonction applicable recevra cet objet comme référence à size_pack<0, pack_t...> (la dernière classe de base, notez le premier 0), ainsi, pack_t sera notre liste de 5 void s.

Enfin, nous avons:

(pack_t(), o) 

qui est juste l'opérateur virgule, donc, il retourne o. L'idée est que nous avons inséré pack_t (le pack de paramètres), à l'intérieur du "pattern", donc, en appliquant ... à l'expression, il sera remplacé par des expressions séparées par des virgules où chaque aspect pack_t sera remplacé par chaque élément dans le Pack paramètre dans le même ordre, de sorte que:

{ (pack_t(), o)... } 

est transformé à:

{ (void(), o), (void(), o), (void(), o), (void(), o), (void(), o) } 

une liste d'initialisation !! Enfin, chaque élément est juste une expression void suivie de l'opérateur coma et seul le second élément de chaque paire sera renvoyé par l'opérateur virgule. Ainsi, l'expression évaluée sera:

return { o, o, o, o, o }; // With its corresponding calls to `forward`. 

Notre liste d'initialisation désirée !!

exemple Coliru:

http://coliru.stacked-crooked.com/a/d6c4ab6c9b203130

Vous ne devez remplacer le type T avec votre classe de liste.

+0

Pourquoi réinventez-vous 'std :: make_integer_sequence'? –

+0

Ok, je ne savais pas qu'il existe. Quoi qu'il en soit, est une fonctionnalité C++ 14, et certaines versions de compilateur encore utilisées aujourd'hui (comme g ++ 4.8.4), n'a pas un support complet pour C++ 14. –

+1

Cool! J'accepte cette réponse, mais vous pourriez aussi être intéressé de voir la réponse que quelqu'un d'autre a donnée à une précédente incarnation de ma question: http://stackoverflow.com/questions/18497122/how-to-initialize-stdarrayt-n- élégamment-if-t-is-not-default-constructible – user1476176

0

std::array<T, N> est une classe modèle représentant un tableau de longueur N d'éléments de type T. Si vous voulez créer un tableau de listes, vous pouvez simplement faire std::array<std::list<T>>, N>N est connu à l'heure de compilation. const std::size_t N ne suffit pas, il doit être constexpr.

Donc non, vous ne pouvez pas faire cela. Vous pouvez cependant utiliser std::vector pour cela.

Si vous postez du code de vos efforts, nous pouvons trouver quelque chose de mieux.

+0

Merci! Je pense qu'il est peut-être plus simple de stocker un tableau de pointeurs std :: dans les listes const-sized, puis de créer les listes avec la taille appropriée en utilisant new(). Je dois m'inquiéter de la nouveauté et de la suppression, mais cela rend la taille garantie que je suis après que possible – user1476176

+0

@ user1476176 Utiliser le pointeur intelligent, il gère l'allocation/désallocation de la mémoire par lui-même. – Zereges