Hier ist ein Versuch, die Iteration über ein Tupel in Komponententeile zu zerlegen.
Erstens, eine Funktion, die eine Reihenfolge von Operationen in der Reihenfolge darstellt. Beachten Sie, dass viele Compiler dies schwer zu verstehen, obwohl es legal ist C++ 11 so weit wie ich kann sagen:
template<class... Fs>
void do_in_order(Fs&&... fs) {
int unused[] = { 0, ((void)std::forward<Fs>(fs)(), 0)... }
(void)unused; // blocks warnings
}
nächstes eine Funktion, die eine std::tuple
nimmt und extrahiert die Indizes benötigt, um jedes Element zugreifen . Auf diese Weise können wir uns später weiterentwickeln.
Als Nebeneffekt, unterstützt mein Code std::pair
und std::array
Iteration:
template<class T>
constexpr std::make_index_sequence<std::tuple_size<T>::value>
get_indexes(T const&)
{ return {}; }
Das Fleisch und Kartoffeln:
template<size_t... Is, class Tuple, class F>
void for_each(std::index_sequence<Is...>, Tuple&& tup, F&& f) {
using std::get;
do_in_order([&]{ f(get<Is>(std::forward<Tuple>(tup))); }...);
}
und die Öffentlichkeit gerichtete Schnittstelle:
template<class Tuple, class F>
void for_each(Tuple&& tup, F&& f) {
auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
während es Tuple
angibt, funktioniert es auf std::array
s und std::pair
s. Es leitet auch die R/L-Wertkategorie des Objekts an das Funktionsobjekt weiter, das es aufruft. Beachten Sie außerdem Folgendes: Wenn Sie eine freie Funktion get<N>
auf Ihrem benutzerdefinierten Typ haben und Sie get_indexes
überschreiben, funktioniert das obige for_each
auf Ihrem benutzerdefinierten Typ.
Wie bereits erwähnt, do_in_order
während ordentlich ist nicht von vielen Compilern unterstützt, wie sie nicht die Lambda mit nicht erweiterten Parameter Packs in Parameter Packs erweitert werden.
Wir können do_in_order
in diesem Fall Inline
template<size_t... Is, class Tuple, class F>
void for_each(std::index_sequence<Is...>, Tuple&& tup, F&& f) {
using std::get;
int unused[] = { 0, ((void)f(get<Is>(std::forward<Tuple>(tup)), 0)... }
(void)unused; // blocks warnings
}
dies nicht viel Ausführlichkeit kosten, aber ich persönlich finde es weniger klar. Die Schattenmagie, wie do_in_order
funktioniert, ist meiner Meinung nach inline verdeckt.
index_sequence
(und unterstützende Vorlagen) ist eine C++ 14-Funktion, die in C++ 11 geschrieben werden kann. Eine solche Implementierung beim Stack-Überlauf zu finden, ist einfach.Ein aktueller Top-Google-Hit ist a decent O(lg(n)) depth implementation, die, wenn ich die Kommentare richtig gelesen habe, die Grundlage für mindestens eine Iteration der tatsächlichen gcc make_integer_sequence
sein kann (die Kommentare weisen auch auf einige weitere Kompilierzeit Verbesserungen rund um die Beseitigung von sizeof...
Anrufe).
Alternativ können wir schreiben:
template<class F, class...Args>
void for_each_arg(F&&f,Args&&...args){
using discard=int[];
(void)discard{0,((void)(
f(std::forward<Args>(args))
),0)...};
}
Und dann:
template<size_t... Is, class Tuple, class F>
void for_each(std::index_sequence<Is...>, Tuple&& tup, F&& f) {
using std::get;
for_each_arg(
std::forward<F>(f),
get<Is>(std::forward<Tuple>(tup))...
);
}
Welche das Handbuch vermeidet erweitern noch weitere Compiler kompiliert auf. Wir übergeben die Is
über den Parameter auto&&i
.
In C++ 1z können wir auch mit einem for_each_arg
Funktionsobjekt verwenden, um den Index fiddling zu beseitigen.
Hängt davon ab, was Sie vorhaben, nachdem Sie darüber "iteriert" haben. – Rapptz
Was würde auf Daten verschiedener Typen iterieren für Sie bedeuten? –
Ich möchte jedes Element "cout" - die einfachste Iteration. Zugriff auf jedes Element für 'cout'-ing ... –