I have multiple classes (Foo
and Bar
here for simplicity)
struct Bar {};
struct Foo {};
and a function that takes a single template parameter and does something based on that type:
template <typename T>
constexpr void doSomething() { cout << "Am I a Foo? " << is_same<T,Foo>::value << endl; }
In my code, I am given a template parameter pack of Foo
s and Bar
s, and I am supposed to call the doSomething()
function on each one of them (I do not care about the order in which the functions are executed).
doStuff<Foo, Bar, Bar>(); // --> True / False / False
So far, the only solution I could come up with is this:
template <typename... Ts>
class Doer;
template <>
struct Doer <> {
static constexpr void doStuff() {}
};
template <typename Head, typename... Tail>
struct Doer <Head, Tail...> {
static constexpr void doStuff() {
doSomething<Head>();
Doer<Tail...>::doStuff();
}
};
template <typename... Ts>
constexpr void doStuff() {
return Doer<Ts...>::doStuff();
}
doStuff<Foo, Bar, Bar>(); // --> True / False / False
It works, but I find it rather messy. I had to use a class template with partial specializations because function templates only support full specialization. I also tried
constexpr void doStuff() { }
template <typename Head, typename... Tail>
constexpr void doStuff() {
doSomething<Head>();
doStuff<Tail...>(); // --> Compile Error
}
but the compiler fails because it can't figure out that doStuff<>()
is actually doStuff()
. If I have arguments in my variadic functions, then the compiler is smart enough to resolve this conflict as it applies template type deduction:
constexpr void doStuff() { }
template <typename Head, typename... Tail>
constexpr void doStuff(Head arg, Tail... args) {
doSomething<Head>();
doStuff(args...);
}
Foo f1;
Bar b1, b2;
doStuff<Foo, Bar, Bar>(f1, b1, b2); // --> True / False / False
Am I missing something? Is there a way to get my variadic function working without using function parameters or class templates?
but the compiler fails because it can't figure out that doStuff<>() is actually doStuff().
What about
template <int = 0>
constexpr void doStuff() { }
template <typename Head, typename... Tail>
constexpr void doStuff() {
doSomething<Head>();
doStuff<Tail...>();
}
?
I mean: if the problem is that, at the end, the template variadic list is empty, transform the ground case in a template version with a template parameter (completely different: integer instead of types) with a default value.
So, when Tail...
is empty, the call doStuff<Tail...>()
, that is doStuff<>()
, matches doStuff<0>()
(considering the default value in first function) so call the ground case.
Anyway: if you can use C++17, you can avoid recursion and, using the power of comma operator together with template folding, you can simply write
template <typename... Ts>
constexpr void doStuff() {
(doSomething<Ts>(), ...);
}
In C++14 you can simulate template folding as follows
template <typename... Ts>
constexpr void doStuff() {
using unused = int[];
(void) unused { 0, ((void)doSomething<Ts>(), 0)... };
}
The preceding solution works also with C++11 but not as constexpr
(but also doSomething()
can't be constexpr
in C++11).
Taking in count that you don't care about the order in which the functions are executed, I propose a C++11 solution that maintain constexpr
and it's based on template pack expansion in a fake-function call (or maybe not fake... see you).
But this require that doSomething()
is constexpr
(so, in C++11, can't be void
) and also doStuff()
can't be void
#include <iostream>
template <typename T>
constexpr std::size_t doSomething ()
{ return sizeof(T); }
template <typename ... Ts>
constexpr int fakeFunc (Ts const & ...)
{ return 0; }
template <typename ... Ts>
constexpr int doStuff ()
{ return fakeFunc( doSomething<Ts>()... ); }
int main()
{
constexpr int a { doStuff<char, short, int, long, long long>() };
(void)a;
}