Comment puis-je parcourir un tuple (en utilisant C ++ 11)? J'ai essayé ce qui suit:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
mais cela ne fonctionne pas:
Erreur 1: désolé, non implémenté: impossible de développer 'Listener ...' dans une liste d'arguments de longueur fixe.
Erreur 2: je ne peux pas apparaître dans une expression constante.
Alors, comment puis-je parcourir correctement les éléments d'un tuple?
Réponses:
Boost.Fusion est une possibilité:
Exemple non testé:
la source
J'ai une réponse basée sur l' itération sur un tuple :
L'idée habituelle est d'utiliser la récursivité à la compilation. En fait, cette idée est utilisée pour créer un printf dont le type est sûr, comme indiqué dans les papiers de tuple d'origine.
Cela peut être facilement généralisé en un
for_each
pour les tuples:Bien que cela demande alors un certain effort pour
FuncT
représenter quelque chose avec les surcharges appropriées pour chaque type que le tuple peut contenir. Cela fonctionne mieux si vous savez que tous les éléments de tuple partageront une classe de base commune ou quelque chose de similaire.la source
enable_if
documentation .for_each
. En fait, je l'ai fait moi-même. :-) Je pense que cette réponse serait plus utile si elle était déjà généralisée.const std::tuple<Tp...>&
.. Si vous n'avez pas l'intention de modifier les tuples pendant l'itération, cesconst
versions suffiront.En C ++ 17, vous pouvez utiliser
std::apply
avec une expression fold :Un exemple complet pour imprimer un tuple:
[Exemple en ligne sur Coliru]
Cette solution résout la question de l'ordre d'évaluation dans la réponse de M. Alaggan .
la source
((std::cout << args << '\n'), ...);
:? Le lambda est appelé une fois avec les éléments tuple décompressés en tant queargs
, mais que se passe-t-il avec les doubles parenthèses?((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n'))
.En C ++ 17, vous pouvez faire ceci:
Cela fonctionne déjà dans Clang ++ 3.9, en utilisant std :: experimental :: apply.
la source
do_something()
- se produisant dans un ordre non spécifié, parce que le pack de paramètres est développé dans un appel de fonction()
, dans lequel les arguments ont un ordre non spécifié? Cela pourrait être très important; J'imagine que la plupart des gens s'attendraient à ce que l'ordre soit garanti dans le même ordre que les membres, c'est-à-dire que les indicesstd::get<>()
. AFAIK, pour obtenir une commande garantie dans de tels cas, l'expansion doit être effectuée à l'intérieur{braces}
. Ai-je tort? Cette réponse met l'accent sur un tel ordre: stackoverflow.com/a/16387374/2757035Utilisez Boost.Hana et les lambdas génériques:
http://coliru.stacked-crooked.com/a/27b3691f55caf271
la source
using namespace boost::fusion
(surtout avecusing namespace std
). Maintenant , il n'y a aucun moyen de savoir sifor_each
c'eststd::for_each
ouboost::fusion::for_each
C ++ introduit des instructions d'expansion à cet effet. Ils étaient à l'origine sur la bonne voie pour C ++ 20 mais ont raté de peu la coupe en raison d'un manque de temps pour la révision du libellé de la langue (voir ici et ici ).
La syntaxe actuellement acceptée (voir les liens ci-dessus) est:
la source
Une manière plus simple, intuitive et conviviale de faire cela en C ++ 17, en utilisant
if constexpr
:Il s'agit de la récursivité à la compilation, similaire à celle présentée par @emsr. Mais cela n'utilise pas SFINAE donc (je pense) il est plus convivial pour le compilateur.
la source
Vous devez utiliser la métaprogrammation de modèle, illustrée ici avec Boost.Tuple:
Dans C ++ 0x, vous pouvez écrire en
print_tuple()
tant que fonction de modèle variadique à la place.la source
Définissez d'abord quelques helpers d'index:
Avec votre fonction, vous souhaitez appliquer sur chaque élément de tuple:
tu peux écrire:
Ou si
foo
retournevoid
, utilisezRemarque: Sur C ++ 14
make_index_sequence
est déjà défini ( http://en.cppreference.com/w/cpp/utility/integer_sequence ).Si vous avez besoin d'un ordre d'évaluation de gauche à droite, envisagez quelque chose comme ceci:
la source
foo
àvoid
avant l'appeloperator,
pour éviter une éventuelle surcharge de l'opérateur pathologique.Voici un moyen simple en C ++ 17 d'itérer des éléments de tuple avec juste une bibliothèque standard:
Exemple:
Production:
Cela peut être étendu pour interrompre conditionnellement la boucle au cas où l'appelable renvoie une valeur (mais fonctionne toujours avec des callables qui ne retournent pas de valeur affectable booléenne, par exemple void):
Exemple:
Production:
la source
Si vous voulez utiliser std :: tuple et que vous avez un compilateur C ++ qui prend en charge les modèles variadiques, essayez le code ci-dessous (testé avec g ++ 4.5). Cela devrait être la réponse à votre question.
boost :: fusion est une autre option, mais elle nécessite son propre type de tuple: boost :: fusion :: tuple. Permet de mieux s'en tenir à la norme! Voici un test:
la puissance des modèles variadiques!
la source
Dans MSVC STL, il existe une fonction _For_each_tuple_element (non documentée):
la source
D'autres ont mentionné des bibliothèques tierces bien conçues vers lesquelles vous pouvez vous tourner. Toutefois, si vous utilisez C ++ sans ces bibliothèques tierces, le code suivant peut vous aider.
Remarque: Le code se compile avec n'importe quel compilateur prenant en charge C ++ 11, et il reste cohérent avec la conception de la bibliothèque standard:
Le tuple n'a pas besoin d'être
std::tuple
, et peut être tout ce qui prend en chargestd::get
etstd::tuple_size
; en particulier,std::array
etstd::pair
peut être utilisé;Le tuple peut être un type de référence ou qualifié cv;
Il a le même comportement que
std::for_each
, et renvoie l'entréeUnaryFunction
;Pour les utilisateurs de C ++ 14 (ou version plus récente),
typename std::enable_if<T>::type
ettypename std::decay<T>::type
pourraient être remplacés par leur version simplifiée,std::enable_if_t<T>
etstd::decay_t<T>
;C ++ 17 (ou une version Laster) utilisateurs,
std::tuple_size<T>::value
pourrait être remplacé par sa version simplifiée,std::tuple_size_v<T>
.Pour les utilisateurs de C ++ 20 (ou version plus récente), la
SFINAE
fonctionnalité peut être implémentée avec leConcepts
.la source
En utilisant
constexpr
etif constexpr
(C ++ 17), c'est assez simple et direct:la source
J'ai peut-être manqué ce train, mais ce sera ici pour référence future.
Voici ma construction basée sur cette réponse et sur cette substance :
Vous l'utilisez ensuite comme suit:
Il pourrait y avoir place pour des améliorations.
Selon le code de OP, cela deviendrait ceci:
la source
De toutes les réponses que je l' ai vu ici, ici et ici , je l' ai aimé @sigidagi façon de mieux de itérer. Malheureusement, sa réponse est très verbeuse, ce qui à mon avis obscurcit la clarté inhérente.
C'est ma version de sa solution qui est plus concise et fonctionne avec
std::tuple
,std::pair
etstd::array
.Démo: coliru
Les C ++ 14
std::make_index_sequence
peuvent être implémentés pour C ++ 11 .la source
Le tuple de boost fournit des fonctions d'assistance
get_head()
etget_tail()
vos fonctions d'assistance peuvent donc ressembler à ceci:comme décrit ici http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html
avec
std::tuple
cela devrait être similaire.En fait, malheureusement,
std::tuple
ne semble pas fournir une telle interface, donc les méthodes suggérées auparavant devraient fonctionner, ou vous auriez besoin de passer à celleboost::tuple
qui présente d'autres avantages (comme les opérateurs io déjà fournis). Bien qu'il y ait un inconvénientboost::tuple
avec gcc - il n'accepte pas encore les modèles variadic, mais cela peut être déjà corrigé car je n'ai pas la dernière version de boost installée sur ma machine.la source
Je suis tombé sur le même problème pour l'itération sur un tuple d'objets de fonction, voici donc une autre solution:
Production:
la source
Une autre option serait d'implémenter des itérateurs pour les tuples. Cela présente l'avantage de pouvoir utiliser une variété d'algorithmes fournis par la bibliothèque standard et des boucles for basées sur une plage. Une approche élégante à cela est expliquée ici https://foonathan.net/2017/03/tuple-iterator/ . L'idée de base est de transformer les tuples en une plage avec
begin()
et desend()
méthodes pour fournir des itérateurs. L'itérateur lui-même renvoie unstd::variant<...>
qui peut ensuite être visité à l'aide destd::visit
.Voici quelques exemples:
Ma mise en œuvre (qui est fortement basée sur les explications dans le lien ci-dessus):
L'accès en lecture seule est également pris en charge en passant un
const std::tuple<>&
àto_range()
.la source
En développant la réponse @Stypox, nous pouvons rendre leur solution plus générique (C ++ 17 et plus). En ajoutant un argument de fonction appelable:
Ensuite, nous avons besoin d'une stratégie pour visiter chaque type.
Commençons par quelques helpers (les deux premiers tirés de cppreference):
variant_ref
est utilisé pour permettre la modification de l'état des tuples.Usage:
Résultat:
Pour être complet, voici mon
Bar
&Foo
:la source