Iterazione su diversi tipi

Dato il seguente codice:

struct Window{ void show(); //stuff }w1, w2, w3; struct Widget{ void show(); //stuff }w4, w5, w6; struct Toolbar{ void show(); //stuff }t1, t2, t3; 

Voglio show un sacco di oggetti:

 for (auto &obj : {w3, w4, w5, t1}) obj.show(); 

Tuttavia questo non viene compilato poiché lo std::initializer_list nel for -loop non può dedurre T e in effetti non esiste un T adatto. Non voglio creare un tipo di cancellazione del tipo a causa della quantità di codice richiesta e del sovraccarico di runtime non necessario. Come faccio a scrivere correttamente il mio ciclo in modo tale che il tipo di object venga dedotto separatamente per ciascun elemento dell’elenco concettuale?

Nel C ++ moderno dovresti usare espressioni di piegatura , per “attraversare” i tuoi argomenti eterogenei applicando la funzione membro:

 auto Printer = [](auto&&... args) { (args.show(), ...); }; Printer(w1, w2, w3, w4, w5, w6, t1, t2, t3); 

dimostrazione

Puoi leggere di più su questo nel mio blog

boost :: fusion è fantastico ma oldskool – soddisfa le carenze di c ++ 03.

Espansione del modello variadico di c ++ 11 in soccorso!

 #include  struct Window{ void show() { std::cout << "Window\n"; } //stuff }w1, w2, w3; struct Widget{ void show() { std::cout << "Widget\n"; } //stuff }w4, w5, w6; struct Toolbar{ void show() { std::cout << "Toolbar\n"; } //stuff }t1, t2, t3; template void call_show(Objects&&...objects) { using expand = int[]; (void) expand { 0, ((void)objects.show(), 0)... }; } auto main() -> int { call_show(w3, w4, w5, t1); return 0; } 

uscita prevista:

 Window Widget Widget Toolbar 

un altro, più generico (richiede c ++ 14):

 // note that i have avoided a function names that look like // one in the standard library. template void for_all(Functor&& f, Objects&&... objects) { using expand = int[]; (void) expand { 0, (f(std::forward(objects)), 0)... }; } 

chiamato così:

 for_all([](auto& thing) { thing.show(); }, w3, w4, w5, t1); 

Un’altra opzione è usare l’algoritmo boost::tuple o std::tuple e boost::fusion::for_each :

 #include  #include  boost::fusion::for_each( boost::tie(w1, w2, w3, w4, w5, w6, t1, t2, t3), // by reference, not a copy [](auto&& t) { t.show(); } ); 

Solo per curiosità, ha confrontato l’output di assemblaggio generato del metodo di Richard Hodges con quanto sopra. Con gcc-4.9.2 -Wall -Wextra -std=gnu++14 -O3 -march=native il codice assembly prodotto è identico.

Basato su https://stackoverflow.com/a/6894436/3484570, questo funziona senza creare una funzione, un boost o un’eredità extra.

Intestazione:

 #include  #include  template inline typename std::enable_if::type for_each(const std::tuple &, FuncT) // Unused arguments are given no names. { } template inline typename std::enable_if::type for_each(const std::tuple& t, FuncT f) { f(std::get(t)); for_each(t, f); } template inline typename std::enable_if::type for_each(std::tuple &&, FuncT) // Unused arguments are given no names. { } template inline typename std::enable_if::type for_each(std::tuple&& t, FuncT f) { f(std::get(t)); for_each(std::move(t), f); } 

cpp:

 struct Window{ void show(){} //stuff }w1, w2, w3; struct Widget{ void show(){} //stuff }w4, w5, w6; struct Toolbar{ void show(){} //stuff }t1, t2, t3; int main() { for_each(std::tie(w3, w4, w5, t1), [](auto &obj){ obj.show(); }); } 

Window , Widget e Toolbar condividono un’interfaccia comune, quindi puoi creare una class astratta e farne ereditare altre classi:

 struct Showable { virtual void show() = 0; // abstract method }; struct Window: Showable{ void show(); //stuff }w1, w2, w3; struct Widget: Showable{ void show(); //stuff }w4, w5, w6; struct Toolbar: Showable{ void show(); //stuff }t1, t2, t3; 

Quindi, puoi creare un array di puntatori a Showable e Showable su di esso:

 int main() { Showable *items[] = {&w3, &w4, &w5, &t1}; for (auto &obj : items) obj->show(); } 

Guardalo online

Raccomando Boost.Hana , che IMHO è la migliore e più flessibile libreria di meta-programmazione disponibile.

 #include  #include  namespace hana = boost::hana; hana::for_each(std::tie(w3, w4, w5, t1), [](auto& obj) { obj.show(); }); 

Penso che boost::variant sia degno di nota. Tanto più ha possibilità di diventare std::variant in C ++ 17.

 int main() { std::vector> items = { &w1, &w4, &t1 }; for (const auto& item : items) { boost::apply_visitor([](auto* v) { v->show(); }, item); } return 0; } 

Una risposta tardiva, ma qui c’è una soluzione generale con C ++ 14 che funziona come boost::fusion::for_each ma non richiede Boost:

 #include  namespace detail { template void tuple_for_each_impl(Tuple&& tup, Function&& fn, std::index_sequence) { using dummy = int[]; static_cast(dummy { 0, (static_cast(fn(std::get(std::forward(tup)))), 0)... }); } } template void tuple_for_each(std::tuple&& tup, Function&& fn) { detail::tuple_for_each_impl(std::forward>(tup), std::forward(fn), std::index_sequence_for{}); } int main() { tuple_for_each(std::tie(w1, w2, w3, w4, w5, w6, t1, t2, t3), [](auto&& arg) { arg.show(); }); } 

Se vuoi ottenere più o meno la stessa cosa senza la std::tuple , puoi creare una variante a funzione singola del codice precedente:

 #include  template void va_for_each(Function&& fn, Args&&... args) { using dummy = int[]; static_cast(dummy { 0, (static_cast(fn(std::forward(args))), 0)... }); } int main() { auto action = [](auto&& arg) { arg.show(); }; va_for_each(action, w1, w2, w3, w4, w5, w6, t1, t2, t3); } 

Lo svantaggio del secondo esempio è che è necessario specificare prima la funzione di elaborazione, quindi non ha lo stesso aspetto del famoso std::for_each . Comunque con il mio compilatore (GCC 5.4.0) usando il -O2 ottimizzazione -O2 , producono lo stesso output di assemblaggio .