4

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 Foos and Bars, 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?

max66
  • 65,235
  • 10
  • 71
  • 111
Touloudou
  • 2,079
  • 1
  • 17
  • 28

1 Answers1

6

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;
 }
max66
  • 65,235
  • 10
  • 71
  • 111