164 Stimmen

Wie kann man über die Elemente eines std::tuple iterieren?

Wie kann ich über ein Tupel iterieren (mit C++11)? Ich habe das Folgende 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: sorry, unimplementiert: kann 'Listener ...' nicht in eine Argumentliste mit fester Länge erweitern.
Fehler 2: i kann nicht in einem konstanten Ausdruck vorkommen.

Wie kann ich also korrekt über die Elemente eines Tupels iterieren?

149voto

emsr Punkte 14381

Ich habe eine Antwort, die auf Iteration über ein Tupel :

#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>(t) << std::endl;
    print<I + 1, Tp...>(t);
  }

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

  print(t);
}

Die übliche Idee ist, die Rekursion zur Kompilierzeit zu verwenden. In der Tat wird diese Idee verwendet, um ein printf zu erstellen, das typsicher ist, wie in den ursprünglichen Tupel-Papieren erwähnt.

Dies kann leicht verallgemeinert werden in eine 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>(t));
    for_each<I + 1, FuncT, Tp...>(t, f);
  }

Allerdings erfordert dies dann einige Anstrengungen, um 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 Tupel-Elemente eine gemeinsame Basisklasse oder etwas Ähnliches verwenden.

147voto

xskxzr Punkte 11829

In C++17 können Sie mit std::apply con Faltenausdruck :

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

Ein vollständiges Beispiel für das 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-Beispiel auf Coliru]

Diese Lösung löst das Problem der Auswertungsreihenfolge in M. Alaggans Antwort .

43voto

Daniel Steck Punkte 975

C++ wird eingeführt Expansionserklärungen zu diesem Zweck. Ursprünglich waren sie für C++20 vorgesehen, haben es aber knapp verpasst, weil die Zeit für die Überarbeitung des Wortlauts fehlte (siehe ici y ici ).

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;
}

31voto

Éric Malenfant Punkte 13670

Boost.Fusion ist eine Möglichkeit:

Ungeprüftes Beispiel:

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

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

30voto

Stypox Punkte 595

Eine einfachere, intuitivere und compilerfreundlichere Möglichkeit, 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>(t) << " ";
    // do things
    if constexpr(I+1 != sizeof...(Tp))
        print<I+1>(t);
}

Dies ist eine Rekursion zur Kompilierzeit, ähnlich wie die von @emsr vorgestellte. Allerdings wird hier nicht SFINAE verwendet, so dass es (meiner Meinung nach) compilerfreundlicher ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X