Yves Calaci
Yves Calaci

Reputation: 1129

Recursive variadic void function using template params in C++11

I'm willing to do something as simple as this:

template <typename I, typename... In>
void bar() {
    // Use I here
    bar<In...>(); // Enable recursion
}

However, this leads to "ambiguous call to overloaded function". What makes me curious is that bellow code works:

template <typename T = void>
void foo() { }

template <int T, int... Tn>
void foo() {
    foo<Tn...>();
}

Why is that? The following does works too:

void foobar() {}

template <typename I, typename... In>
void foobar(I i, In... in) {
    foobar(in...);
}

So, whats the simplest way to achieve recursion, given this function signature WITHOUT USING ANY BRACED-INIT-LIST TECHNIQUES:

template <typename I, typename... In>
void bar();

Upvotes: 2

Views: 205

Answers (1)

max66
max66

Reputation: 66210

You can solve in the same way foo() solve: when the Tn... list is empty, foo<>() is called, so the foo() version with a type template parameter (but with a default value) is called (the compiler match foo<>() with foo<void>()).

For bar() you can switch integer and types.

You can add a terminal version that receive a non-type template parameter with a default value

template <int = 0>
void bar()
 { }

so when you call bar() with a empty In... type list, the compiler match the call bar<>() with bar<0>().

--- EDIT ---

The OP ask

If foo<>() matches with foo<void>(), why doesnt bar<>() match with bar<void>() (template <typename Dummy = void> instead of template <int = 0>)? In other words, why does it have to be a template <int = 0> (of int type)?

First of all, why foo() works?

Because there are a foo() variadic template function that receive one or more integers and a foo() template that receive a single type (defaulted).

So, when you call foo<>, it doesn't match the integer variadic version (because at least one integer in required) and match the type version (activating the void default type).

Second: why bar() doesn't works adding a version that receive a single (and defaulted) type?

Because you have a variadic template function that receive one or more types

template <typename I, typename... In>
void bar ()
 { bar<In...>(); }

so if you add a version that receive one type (defaulted)

template <typename = void>
void bar ()
 { }

you get into trouble the compiler: wich version choose when you call bar() with the last type?

I mean: you call bar<int, long>() and only the variadic version match.

But the variadic version call bar<long>().

The problem now is that both versions of bar() (the variadic one and the one with a single type) match.

So the compiler give you an error.

The trick is to create a bar() version with a single defaulted template parameter that can match the empty list call (foo<>()) but doesn't clash with the type variadic version.

A possible solution is the int one, but you can choose other types (char, long, unsigned long long and others) or even a version based on a template-template defaulted parameter.

I mean... instead of

template <int = 0>
void bar ()
 { }

you can use

template <template <typename...> class = std::vector>
void bar()
 { }

This isn't a suggestion: I think the int = 0 is the simpler to write and understand. Just to show another possible way.

The point is adding a template that receive a single defaulted value but not a typename one.

Upvotes: 3

Related Questions