Reputation: 303890
I want to write a class method that takes a template parameter pack, but zero arguments, and "iterate" over the types:
struct Bar {
template <typename T, typename... Ts>
void foo() {
// something with T that involves Bar's members
foo<Ts...>();
}
};
What is the preferred way to implement this?
Upvotes: 7
Views: 945
Reputation: 218268
You may use the following:
struct Bar {
template <typename... Ts>
void foo() {
int dummy[] = {0 /*Manage case where Ts is empty*/,
(bar<Ts>(), void() /* To avoid overload `operator,` */, 0)...};
(void) dummy; // suppress warning for unused variable.
}
template <typename T>
void bar()
{
// something with T that involves Bar's members
}
};
In C++17, it can be simplified with Folding expression:
struct Bar {
template <typename... Ts>
void foo() {
(static_cast<void>(bar<Ts>()), ...);
}
template <typename T>
void bar()
{
// something with T that involves Bar's members
}
};
Upvotes: 3
Reputation: 275896
template<class...Fs>
void do_in_order(Fs&&...fs) {
int _[]={0, ( std::forward<Fs>(fs)(), void(), 0 )...};
(void)_;
}
hides the syntax required to execute a pack of function objects in left to right order.
Then:
struct Bar {
template <class... Ts>
void foo() {
do_in_order([&]{
using T = Ts;
// code
}...);
}
};
and in a conforming compiler, we will run the // code
with T
being each type from left to right.
Note that some compilers claiming to be C++11 compilers may fail to compile the above.
The advantage of this technique is that it hides the nasty "expand and evaluate templates" code within a function with a clear name. You write do_in_order
once, and it usually suffices for almost every use of that array-expansion trick.
There are a two important reasons to use this kind of esoteric syntax instead of the "more simple" recursive solutions.
First, it makes things easier for the optimizer. Optimizers sometimes give up after a pile of recursive calls.
Second, the sum of the lengths names of the function signatures for the traditional recursive functions grow at O(n^2). If you use helper types, the total length of the names is also O(n^2). Unless you are careful, this can cause compile time, link time, and binary size bloat.
In C++1z there are plans for some "fold" syntax that may make the esoteric parts of the above less esoteric.
Upvotes: 3
Reputation: 3460
I like overloaded functions and using a typelist:
#include <iostream>
#include <typeinfo>
template <typename ...Ts> struct typelist { };
void foo_impl(typelist<> )
{
// we are finished
}
template <typename T, typename ...Ts>
void foo_impl(typelist<T, Ts...> )
{
std::cout << typeid(T).name() << ", ";
foo_impl(typelist<Ts...>{});
}
template <typename ...Ts>
void foo()
{
std::cout << "called with <";
foo_impl(typelist<Ts...>{});
std::cout << ">" << std::endl;
}
int main()
{
foo<int, char, float>();
}
Upvotes: 0