Wie kann ich über ein Tupel iterieren (mit C ++ 11)? Ich habe folgendes versucht:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
aber das funktioniert nicht:
Fehler 1: Entschuldigung, nicht implementiert: 'Listener ...' kann nicht in eine Argumentliste mit fester Länge erweitert werden.
Fehler 2: Ich kann nicht in einem konstanten Ausdruck erscheinen.
Wie iteriere ich also korrekt über die Elemente eines Tupels?
Antworten:
Boost.Fusion ist eine Möglichkeit:
Ungetestetes Beispiel:
quelle
Ich habe eine Antwort, die auf dem Iterieren über ein Tupel basiert :
Die übliche Idee ist die Rekursion der Kompilierungszeit. Tatsächlich wird diese Idee verwendet, um einen Ausdruck zu erstellen, der typsicher ist, wie in den Original-Tupelpapieren angegeben.
Dies kann leicht in ein
for_each
für Tupel verallgemeinert werden :Dies erfordert dann jedoch einige Anstrengungen, um
FuncT
für jeden Typ, den das Tupel enthalten könnte, etwas mit den entsprechenden Überladungen darzustellen. Dies funktioniert am besten, wenn Sie wissen, dass alle Tupelelemente eine gemeinsame Basisklasse oder ähnliches haben.quelle
enable_if
Dokumentation .for_each
. Tatsächlich habe ich es selbst gemacht. :-) Ich denke, diese Antwort wäre nützlicher, wenn sie bereits verallgemeinert wäre.const std::tuple<Tp...>&
.. Wenn Sie nicht beabsichtigen, Tupel während der Iteration zu ändern,const
reichen diese Versionen aus.In C ++ 17 können Sie
std::apply
mit dem Fold-Ausdruck Folgendes verwenden :Ein vollständiges Beispiel zum Drucken eines Tupels:
[Online Beispiel auf Coliru]
Diese Lösung löst das Problem der Bewertungsreihenfolge in der Antwort von M. Alaggan .
quelle
((std::cout << args << '\n'), ...);
? Das Lambda wird einmal mit den entpackten Tupelelementen aufgerufenargs
, aber was ist mit den doppelten Klammern los?((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n'))
.In C ++ 17 können Sie dies tun:
Dies funktioniert bereits in Clang ++ 3.9 mit std :: experimentelle :: apply.
quelle
do_something()
- in einer nicht angegebenen Reihenfolge erfolgt, weil das Parameterpaket innerhalb eines Funktionsaufrufs erweitert wird()
, bei dem Argumente eine nicht angegebene Reihenfolge haben? Das könnte sehr bedeutsam sein; Ich würde mir vorstellen, dass die meisten Leute erwarten würden, dass die Bestellung garantiert in derselben Reihenfolge wie die Mitglieder erfolgt, dh wie die Indizesstd::get<>()
. AFAIK, um in solchen Fällen eine garantierte Bestellung zu erhalten, muss die Erweiterung innerhalb erfolgen{braces}
. Liege ich falsch? Diese Antwort betont eine solche Reihenfolge: stackoverflow.com/a/16387374/2757035Verwenden Sie Boost.Hana und generische Lambdas:
http://coliru.stacked-crooked.com/a/27b3691f55caf271
quelle
using namespace boost::fusion
(besonders zusammen mitusing namespace std
). Jetzt gibt es keine Möglichkeit zu wissen, ob dasfor_each
iststd::for_each
oderboost::fusion::for_each
Zu diesem Zweck führt C ++ Erweiterungsanweisungen ein . Sie waren ursprünglich auf dem richtigen Weg für C ++ 20, haben den Schnitt jedoch nur knapp verpasst, da nicht genügend Zeit für die Überprüfung der Sprachformulierung vorhanden war (siehe hier und hier ).
Die derzeit vereinbarte Syntax (siehe die obigen Links) lautet:
quelle
Eine einfachere, intuitivere und compilerfreundlichere Methode in C ++ 17 mit
if constexpr
:Dies ist eine Rekursion zur Kompilierungszeit, ähnlich der von @emsr. Da dies jedoch kein SFINAE verwendet, ist es (glaube ich) compilerfreundlicher.
quelle
Sie müssen die Metaprogrammierung von Vorlagen verwenden, die hier mit Boost.Tuple angezeigt wird:
In C ++ 0x können Sie
print_tuple()
stattdessen als variable Vorlagenfunktion schreiben .quelle
Definieren Sie zunächst einige Index-Helfer:
Mit Ihrer Funktion möchten Sie auf jedes Tupelelement anwenden:
Du kannst schreiben:
Oder wenn
foo
zurückkommtvoid
, verwenden SieHinweis: Unter C ++ 14
make_index_sequence
ist bereits definiert ( http://en.cppreference.com/w/cpp/utility/integer_sequence ).Wenn Sie eine Bewertungsreihenfolge von links nach rechts benötigen, sollten Sie Folgendes berücksichtigen:
quelle
foo
to setzen , um eine mögliche Überlastung des pathologischen Operators zu vermeiden.void
operator,
Hier ist eine einfache C ++ 17-Methode zum Durchlaufen von Tupelelementen mit nur einer Standardbibliothek:
Beispiel:
Ausgabe:
Dies kann erweitert werden, um die Schleife bedingt zu unterbrechen, falls der Callable einen Wert zurückgibt (aber immer noch mit Callables funktioniert, die keinen zuweisbaren Bool-Wert zurückgeben, z. B. void):
Beispiel:
Ausgabe:
quelle
Wenn Sie std :: tuple verwenden möchten und einen C ++ - Compiler haben, der verschiedene Vorlagen unterstützt, versuchen Sie es mit dem folgenden Code (getestet mit g ++ 4.5). Dies sollte die Antwort auf Ihre Frage sein.
boost :: fusion ist eine weitere Option, erfordert jedoch einen eigenen Tupeltyp: boost :: fusion :: tuple. Halten wir uns besser an den Standard! Hier ist ein Test:
Die Kraft variabler Vorlagen!
quelle
In MSVC STL gibt es eine _For_each_tuple_element-Funktion (nicht dokumentiert):
quelle
Andere haben einige gut gestaltete Bibliotheken von Drittanbietern erwähnt, an die Sie sich wenden können. Wenn Sie jedoch C ++ ohne diese Bibliotheken von Drittanbietern verwenden, kann der folgende Code hilfreich sein.
Hinweis: Der Code wird mit jedem Compiler kompiliert, der C ++ 11 unterstützt, und bleibt konsistent mit dem Design der Standardbibliothek:
Das Tupel muss nicht sein
std::tuple
und kann stattdessen alles sein, wasstd::get
und unterstütztstd::tuple_size
; insbesonderestd::array
undstd::pair
kann verwendet werden;Das Tupel kann ein Referenztyp oder ein Lebenslauf sein.
Es hat ein ähnliches Verhalten wie
std::for_each
und gibt die Eingabe zurückUnaryFunction
.Für C ++ 14 (oder Laster-Version) Benutzer
typename std::enable_if<T>::type
undtypename std::decay<T>::type
könnte durch ihre vereinfachte Version ersetzt werden,std::enable_if_t<T>
undstd::decay_t<T>
;Für C ++ 17 (oder Laster-Version) Benutzer
std::tuple_size<T>::value
könnte durch die vereinfachte Version ersetzt werdenstd::tuple_size_v<T>
.Für Benutzer von C ++ 20 (oder einer Laster-Version)
SFINAE
könnte die Funktion mit dem implementiert werdenConcepts
.quelle
Mit
constexpr
undif constexpr
(C ++ 17) ist dies ziemlich einfach und unkompliziert:quelle
Ich hätte diesen Zug vielleicht verpasst, aber dies wird hier sein, um später darauf zurückgreifen zu können.
Hier ist mein Konstrukt basierend auf dieser Antwort und diesem Kern :
Sie verwenden es dann wie folgt:
Es könnte Raum für Verbesserungen geben.
Gemäß dem OP-Code würde dies folgendermaßen aussehen:
quelle
Von all den Antworten, die ich hier, hier und hier gesehen habe , mochte ich @sigidagis Art, am besten zu iterieren. Leider ist seine Antwort sehr ausführlich, was meiner Meinung nach die inhärente Klarheit verdeckt.
Dies ist meine Version seiner Lösung , die prägnanten und arbeitet mit
std::tuple
,std::pair
undstd::array
.Demo: coliru
C ++ 14
std::make_index_sequence
kann für C ++ 11 implementiert werden .quelle
Auftrieb des Tupels stellt Hilfsfunktionen
get_head()
undget_tail()
so Ihre Hilfsfunktionen wie folgt aussehen:wie hier beschrieben http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html
mit
std::tuple
ihm sollte ähnlich sein.Tatsächlich
std::tuple
scheint eine solche Schnittstelle leider nicht verfügbar zu sein, daher sollten die zuvor vorgeschlagenen Methoden funktionieren, oder Sie müssten zu einer wechseln,boost::tuple
die andere Vorteile bietet (wie z. B. bereits bereitgestellte Io-Operatoren). Obwohl es einen Nachteil vonboost::tuple
gcc gibt - es akzeptiert noch keine variablen Vorlagen, aber das kann bereits behoben sein, da ich nicht die neueste Version von Boost auf meinem Computer installiert habe.quelle
Ich bin auf dasselbe Problem gestoßen, als ich über ein Tupel von Funktionsobjekten iteriert habe. Hier ist eine weitere Lösung:
Ausgabe:
quelle
Eine andere Möglichkeit wäre, Iteratoren für Tupel zu implementieren. Dies hat den Vorteil, dass Sie eine Vielzahl von Algorithmen verwenden können, die von der Standardbibliothek bereitgestellt werden und bereichsbasiert für Schleifen sind. Ein eleganter Ansatz hierzu wird hier erklärt: https://foonathan.net/2017/03/tuple-iterator/ . Die Grundidee besteht darin, Tupel in einen Bereich mit
begin()
undend()
Methoden zur Bereitstellung von Iteratoren umzuwandeln. Der Iterator selbst gibt ein zurück,std::variant<...>
das dann mit besucht werden kannstd::visit
.Hier einige Beispiele:
Meine Implementierung (die stark auf den Erklärungen im obigen Link basiert):
Der schreibgeschützte Zugriff wird auch durch Übergabe von a
const std::tuple<>&
an unterstütztto_range()
.quelle
Wenn wir die @ Stypox-Antwort erweitern, können wir ihre Lösung allgemeiner gestalten (ab C ++ 17). Durch Hinzufügen eines aufrufbaren Funktionsarguments:
Dann brauchen wir eine Strategie, um jeden Typ zu besuchen.
Beginnen wir mit einigen Helfern (die ersten beiden stammen aus cppreference):
variant_ref
wird verwendet, um den Status von Tupeln zu ändern.Verwendung:
Ergebnis:
Der Vollständigkeit halber hier meine
Bar
&Foo
:quelle