En développant la réponse de @Stypox, nous pouvons rendre leur solution plus générique (à partir de C++17). En ajoutant un argument de fonction appelable :
template<size_t I = 0, typename... Tp, typename F>
void for_each_apply(std::tuple<Tp...>& t, F &&f) {
f(std::get<I>(t));
if constexpr(I+1 != sizeof...(Tp)) {
for_each_apply<I+1>(t, std::forward<F>(f));
}
}
Ensuite, nous avons besoin d'une stratégie pour visiter chaque type.
Commençons par quelques aides (les deux premières proviennent de cppreference) :
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
template<class ... Ts> struct variant_ref { using type = std::variant<std::reference_wrapper<Ts>...>; };
variant_ref
est utilisé pour permettre la modification de l'état des tuples.
Utilisation :
std::tuple<Foo, Bar, Foo> tuples;
for_each_apply(tuples,
[](variant_ref<Foo, Bar>::type &&v) {
std::visit(overloaded {
[](Foo &arg) { arg.foo(); },
[](Bar const &arg) { arg.bar(); },
}, v);
});
Résultat :
Foo0
Bar
Foo0
Foo1
Bar
Foo1
Pour être complet, voici mes Bar
& Foo
:
struct Foo {
void foo() {std::cout << "Foo" << i++ << std::endl;}
int i = 0;
};
struct Bar {
void bar() const {std::cout << "Bar" << std::endl;}
};