2

I've been frustrated by a simple variadic template function:

constexpr size_t num_args () {
    return 0;
}

template <typename H, typename... T>
constexpr size_t num_args () {
    return 1 + num_args <T...> ();
}

int main () {
    std :: cout << num_args <int, int, int> ();
}

The above doesn't compile, see above linked question and followup for details, yet the following function DOES compile

template <typename T, typename... Args> void foo (T, Args...);

template <typename... Args> void foo (int, Args...);

void foo () {}

template <typename... Args>
void foo (int x, Args... args)
{
    std :: cout << "int:" << x;
    foo (args...);
}

template <typename T, typename... Args>
void foo (T x, Args... args)
{
    std :: cout << " T:" << x;
    foo (args...);
}

foo (int (123), float (123)); // Prints "int:123 T:123.0"

Both seem to be using the same language mechanism, but why is the first one bad when the second one is good?

Community
  • 1
  • 1
spraff
  • 32,570
  • 22
  • 121
  • 229

2 Answers2

4

The difference is that the first function

constexpr size_t num_args () {
    return 0;
}

is not a template, so it can never be called like this

num_args <T...> ();
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
0

I'm starting to think that the difference is that

foo (args...);

exploits overloading whereas

num_args <T...> ();

exploits specialisation.

Overloading can handle the base case but specialisation can't for function templates (but it can for classes) which is why auxilliary_class<Args...>::value is idiomatic.

spraff
  • 32,570
  • 22
  • 121
  • 229