Wie können Sie über die Elemente eines std::tuple iterieren?

Lesezeit: 7 Minuten

Wie kann ich über ein Tupel iterieren (mit C++11)? Folgendes habe ich versucht:

for(int i=0; i<std::tuple_size<T...>::value; ++i) 
  std::get<i>(my_tuple).do_sth();

aber das geht nicht:

Fehler 1: Entschuldigung, nicht implementiert: „Listener …“ kann nicht in eine Argumentliste mit fester Länge erweitert werden.
Fehler 2: i kann nicht in einem konstanten Ausdruck erscheinen.

Also, wie iteriere ich korrekt über die Elemente eines Tupels?

  • Darf ich fragen, wie Sie in C++0x kompilieren? Soweit ich weiß, ist es weder veröffentlicht noch fertig.

    – Burkhard

    29. Juli 2009 um 6:09 Uhr

  • g++ enthält seit Version 4.3 experimentelle Unterstützung einiger C++0X-Features, einschließlich variadischer Templates. Andere Compiler machen dasselbe (mit unterschiedlichen Feature-Sets, wenn Sie sie in der Produktion verwenden möchten, sind Sie zurück in den 90er Jahren mit einer breiten Palette an Unterstützung für bahnbrechende Dinge)

    – Ein Programmierer

    29. Juli 2009 um 7:10 Uhr

  • Ich verwende g++ Version 4.4 mit std=c++0x

    1521237

    30. Juli 2009 um 10:33 Uhr

  • Diese Frage erfordert ein C++11-Update.

    – Allmächtig

    3. Februar 2013 um 4:04 Uhr

  • @Omnifarious jetzt braucht es ein C++14-Update

    – pfeffer_chico

    19. April 2014 um 15:38 Uhr

Wie konnen Sie uber die Elemente eines stdtuple iterieren
emsr

Ich habe eine Antwort, die auf dem Iterieren über ein Tupel basiert:

#include <tuple>
#include <utility> 
#include <iostream>

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  print(std::tuple<Tp...>& t)
  { }

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  print(std::tuple<Tp...>& t)
  {
    std::cout << std::get<I>
    print<I + 1, Tp...>
  }

int
main()
{
  typedef std::tuple<int, float, double> T;
  T t = std::make_tuple(2, 3.14159F, 2345.678);

  print
}

Die übliche Idee ist die Rekursion zur Kompilierzeit. Tatsächlich wird diese Idee verwendet, um ein printf zu erstellen, das typsicher ist, wie in den ursprünglichen Tupelpapieren angegeben.

Dies lässt sich leicht zu a verallgemeinern for_each für Tupel:

#include <tuple>
#include <utility> 

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
  { }

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...>& t, FuncT f)
  {
    f(std::get<I>
    for_each<I + 1, FuncT, Tp...>(t, f);
  }

Dies erfordert dann allerdings etwas Aufwand FuncT etwas mit den entsprechenden Überladungen für jeden Typ darstellen, den das Tupel enthalten könnte. Dies funktioniert am besten, wenn Sie wissen, dass alle Tupelelemente eine gemeinsame Basisklasse oder etwas Ähnliches haben.

  • Danke für das schöne einfache Beispiel. Für C++-Anfänger, die nach Hintergrundinformationen zur Funktionsweise suchen, siehe SFINAE und enable_if Dokumentation.

    – Faheem Mitha

    12. Februar 2012 um 5:28 Uhr

  • Dies könnte leicht zu einem Generikum verallgemeinert werden for_each. Tatsächlich habe ich es selbst gemacht. 🙂 Ich denke, diese Antwort wäre nützlicher, wenn sie bereits verallgemeinert wäre.

    – Allmächtig

    3. Februar 2013 um 11:27 Uhr

  • Dort habe ich die Verallgemeinerung hinzugefügt, weil ich tatsächlich eine brauchte und ich denke, dass es für andere nützlich wäre, sie zu sehen.

    – Allmächtig

    4. Februar 2013 um 7:06 Uhr

  • Hinweis: Möglicherweise benötigen Sie auch Versionen mit const std::tuple<Tp...>&.. Wenn Sie nicht beabsichtigen, Tupel während der Iteration zu ändern, sind diese const Versionen genügen.

    – tödliche Gitarre

    6. Juni 2013 um 19:17 Uhr

  • Nicht wie geschrieben.. Sie könnten eine Version mit umgedrehter Indizierung erstellen – beginnen Sie bei I = sizeof … (Tp) und zählen Sie herunter. Geben Sie dann explizit eine maximale Anzahl von Argumenten an. Sie könnten auch eine Version erstellen, die bei einem Tag-Typ kaputt geht, sagen wir break_t. Dann würden Sie ein Objekt dieses Tag-Typs in Ihr Tupel einfügen, wenn Sie das Drucken stoppen wollten. Oder Sie könnten einen Stopptyp als Vorlagenparameter bereitstellen. Offensichtlich konnten Sie nicht zur Laufzeit brechen.

    – Emsr

    12. Dezember 2014 um 19:29 Uhr

In C++17 können Sie verwenden std::apply mit Ausdruck falten:

std::apply([](auto&&... args) {((/* args.dosomething() */), ...);}, the_tuple);

Ein vollständiges Beispiel zum Drucken eines Tupels:

#include <tuple>
#include <iostream>

int main()
{
    std::tuple t{42, 'a', 4.2}; // Another C++17 feature: class template argument deduction
    std::apply([](auto&&... args) {((std::cout << args << '\n'), ...);}, t);
}

[Online Example on Coliru]

Diese Lösung löst das Problem der Bewertungsreihenfolge in der Antwort von M. Alaggan.

  • Können Sie erklären, was hier passiert: ((std::cout << args << '\n'), ...); ? Das Lambda wird einmal aufgerufen, wobei die Tupelelemente als entpackt werden argsaber was hat es mit den doppelten Klammern auf sich?

    – helmesjo

    19. Januar 2019 um 22:17 Uhr

  • @helmesjo Es wird zu einem Kommaausdruck erweitert ((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n')) Hier.

    – xskxzr

    20. Januar 2019 um 3:37 Uhr


  • Beachten Sie, dass Sie für den Fall, dass Sie Dinge tun möchten, die in einem Komma-Ausdruck nicht zulässig sind (wie das Deklarieren von Variablen und Blöcken), all dies in eine Methode packen und sie einfach innerhalb des gefalteten Komma-Ausdrucks aufrufen können.

    – Miral

    27. Februar 2020 um 7:00 Uhr

1647204608 690 Wie konnen Sie uber die Elemente eines stdtuple iterieren
Daniel Steck

C++ wird eingeführt Erweiterungserklärungen für diesen Zweck. Sie waren ursprünglich auf dem Weg zu C++20, verpassten die Kürzung jedoch knapp, da die Zeit für die Überprüfung der Sprachformulierung fehlte (siehe Hier und Hier).

Die derzeit vereinbarte Syntax (siehe die obigen Links) lautet:

{
    auto tup = std::make_tuple(0, 'a', 3.14);
    template for (auto elem : tup)
        std::cout << elem << std::endl;
}

Wie konnen Sie uber die Elemente eines stdtuple iterieren
Eric Malenfant

Boost.Fusion ist eine möglichkeit:

Ungetestetes Beispiel:

struct DoSomething
{
    template<typename T>
    void operator()(T& t) const
    {
        t.do_sth();
    }
};

tuple<....> t = ...;
boost::fusion::for_each(t, DoSomething());

In C++17 können Sie dies tun:

std::apply([](auto ...x){std::make_tuple(x.do_something()...);} , the_tuple);

Dies funktioniert bereits in Clang++ 3.9 mit std::experimental::apply.

  • Führt dies nicht zur Iteration – dh Aufrufen von do_something() – in unbestimmter Reihenfolge auftreten, da das Parameterpaket innerhalb eines Funktionsaufrufs expandiert wird (), wobei Argumente eine unbestimmte Reihenfolge haben? Das könnte sehr bedeutsam sein; Ich würde mir vorstellen, dass die meisten Leute erwarten würden, dass die Reihenfolge garantiert in der gleichen Reihenfolge wie die Mitglieder erfolgt, dh wie die Indizes std::get<>(). AFAIK, um in solchen Fällen eine garantierte Bestellung zu erhalten, muss die Erweiterung innerhalb von erfolgen {braces}. Liege ich falsch? Diese Antwort betont eine solche Reihenfolge: stackoverflow.com/a/16387374/2757035

    – Unterstrich_d

    9. Oktober 2016 um 14:37 Uhr


Verwenden Sie Boost.Hana und generische Lambdas:

#include <tuple>
#include <iostream>
#include <boost/hana.hpp>
#include <boost/hana/ext/std/tuple.hpp>

struct Foo1 {
    int foo() const { return 42; }
};

struct Foo2 {
    int bar = 0;
    int foo() { bar = 24; return bar; }
};

int main() {
    using namespace std;
    using boost::hana::for_each;

    Foo1 foo1;
    Foo2 foo2;

    for_each(tie(foo1, foo2), [](auto &foo) {
        cout << foo.foo() << endl;
    });

    cout << "foo2.bar after mutation: " << foo2.bar << endl;
}

http://coliru.stacked-crooked.com/a/27b3691f55caf271

  • Führt dies nicht zur Iteration – dh Aufrufen von do_something() – in unbestimmter Reihenfolge auftreten, da das Parameterpaket innerhalb eines Funktionsaufrufs expandiert wird (), wobei Argumente eine unbestimmte Reihenfolge haben? Das könnte sehr wichtig sein; Ich würde mir vorstellen, dass die meisten Leute erwarten würden, dass die Reihenfolge garantiert in der gleichen Reihenfolge wie die Mitglieder erfolgt, dh wie die Indizes std::get<>(). AFAIK, um in solchen Fällen eine garantierte Bestellung zu erhalten, muss die Erweiterung innerhalb von erfolgen {braces}. Liege ich falsch? Diese Antwort betont eine solche Reihenfolge: stackoverflow.com/a/16387374/2757035

    – Unterstrich_d

    9. Oktober 2016 um 14:37 Uhr


Eine einfachere, intuitivere und Compiler-freundlichere Methode, dies in C++17 zu tun, ist die Verwendung von if constexpr:

// prints every element of a tuple
template<size_t I = 0, typename... Tp>
void print(std::tuple<Tp...>& t) {
    std::cout << std::get<I>
    // do things
    if constexpr(I+1 != sizeof...(Tp))
        print<I+1>
}

Dies ist eine Rekursion zur Kompilierzeit, ähnlich der von @emsr. Aber das verwendet SFINAE nicht, also ist es (glaube ich) Compiler-freundlicher.

998970cookie-checkWie können Sie über die Elemente eines std::tuple iterieren?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy