web-dev-qa-db-fra.com

Itération sur différents types

Étant donné le code suivant:

struct Window{
    void show();
    //stuff
}w1, w2, w3;

struct Widget{
    void show();
    //stuff
}w4, w5, w6;

struct Toolbar{
    void show();
    //stuff
}t1, t2, t3;

Je veux show un tas d'articles:

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

Cependant, cela ne compile pas puisque le std::initializer_list<T> dans la boucle for- ne peut pas déduire T et en fait il n'y a pas vraiment de T qui conviendrait. Je ne veux pas créer un type d'effacement de type en raison de la quantité de code requise et de la surcharge d'exécution inutile. Comment écrire correctement ma boucle pour que le type de obj soit déduit séparément pour chaque élément de la liste conceptuelle?

62
nwp

Dans le C++ moderne, vous utiliseriez des expressions de repli , pour "parcourir" vos arguments hétérogènes en appliquant la fonction membre:

auto Printer = [](auto&&... args) {
    (args.show(), ...);
};

Printer(w1, w2, w3, w4, w5, w6, t1, t2, t3);

Démo

Vous pouvez en savoir plus à ce sujet dans mon blog

58
Nikos Athanasiou

boost :: fusion est génial mais oldskool - il répond aux carences en c ++ 03.

l'expansion du modèle variadic de c ++ 11 à la rescousse!

#include <iostream>

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<class...Objects>
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;
}

production attendue:

Window
Widget
Widget
Toolbar

une autre manière plus générique (nécessite c ++ 14):

// note that i have avoided a function names that look like
// one in the standard library.

template<class Functor, class...Objects>
void for_all(Functor&& f, Objects&&... objects)
{
    using expand = int[];
    (void) expand { 0, (f(std::forward<Objects>(objects)), 0)... };

}

appelé ainsi:

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

Une autre option consiste à utiliser boost::Tuple ou std::Tuple et boost::fusion::for_each algorithme:

#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/adapted/boost_Tuple.hpp>

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

Par curiosité, nous avons comparé la sortie d'assemblage générée par la méthode de Richard Hodges avec celle ci-dessus. Avec gcc-4.9.2 -Wall -Wextra -std=gnu++14 -O3 -march=native le code d'assemblage produit est identique.

21
Maxim Egorushkin

Basé sur https://stackoverflow.com/a/6894436/348457 cela fonctionne sans créer de fonction supplémentaire, de boost ou d'héritage.

Entête:

#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(const 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(const std::Tuple<Tp...>& t, FuncT f)
  {
    f(std::get<I>(t));
    for_each<I + 1, FuncT, Tp...>(t, f);
  }

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...>(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();
    });
}
15
nwp

Window, Widget et Toolbar partagent une interface commune, vous pouvez donc créer une classe abstraite et en faire hériter d'autres classes:

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;

Ensuite, vous pouvez créer un tableau de pointeurs vers Showable, et itérer dessus:

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

Voir le travail en ligne

11
GingerPlusPlus

Je recommande Boost.Hana , quelle IMHO est la bibliothèque de méta-programmation de modèle la meilleure et la plus flexible disponible.

#include <boost/hana/ext/std/Tuple.hpp>
#include <boost/hana.hpp>

namespace hana = boost::hana;

hana::for_each(std::tie(w3, w4, w5, t1), [](auto& obj) { obj.show(); });
8
Brian Rodriguez

Je pense boost::variant mérite d'être mentionné. D'autant plus qu'il a des chances de devenir std::variant en C++ 17.

int main()
{
  std::vector<boost::variant<Window*, Widget*, Toolbar*>> items = { &w1, &w4, &t1 };

  for (const auto& item : items)
  {
    boost::apply_visitor([](auto* v) { v->show(); }, item);
  }
  return 0;
}
4
Mikhail

Une réponse tardive mais voici une solution générale avec C++ 14 qui fonctionne comme le boost::fusion::for_each mais ne nécessite pas de boost:

#include <Tuple>

namespace detail {
template<typename Tuple, typename Function, std::size_t... Is>
void Tuple_for_each_impl(Tuple&& tup, Function&& fn, std::index_sequence<Is...>) {
    using dummy = int[];
    static_cast<void>(dummy {
        0, (static_cast<void>(fn(std::get<Is>(std::forward<Tuple>(tup)))), 0)...
    });
}
}

template<typename Function, typename... Args>
void Tuple_for_each(std::Tuple<Args...>&& tup, Function&& fn) {
    detail::Tuple_for_each_impl(std::forward<std::Tuple<Args...>>(tup),
            std::forward<Function>(fn), std::index_sequence_for<Args...>{});
}

int main() {
    Tuple_for_each(std::tie(w1, w2, w3, w4, w5, w6, t1, t2, t3), [](auto&& arg) {
        arg.show();
    });
}

Si vous voulez réaliser plus ou moins la même chose sans std::Tuple , vous pouvez créer une variante à fonction unique du code ci-dessus:

#include <utility>

template<typename Function, typename... Args>
void va_for_each(Function&& fn, Args&&... args) {
    using dummy = int[];
    static_cast<void>(dummy {
        0, (static_cast<void>(fn(std::forward<Args>(args))), 0)...
    });
}

int main() {
    auto action = [](auto&& arg) { arg.show(); };
    va_for_each(action, w1, w2, w3, w4, w5, w6, t1, t2, t3);
}

L'inconvénient du deuxième exemple est qu'il nécessite de spécifier d'abord la fonction de traitement, il n'a donc pas le même aspect que le bien connu std::for_each . Quoi qu'il en soit avec mon compilateur (GCC 5.4.0) en utilisant -O2 niveau d'optimisation, ils produisent le même sortie d'assemblage .

0
Akira