É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?
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);
Vous pouvez en savoir plus à ce sujet dans mon blog
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);
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.
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();
});
}
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();
}
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(); });
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;
}
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 .