1

I am trying to grasp and understand the concept of variadic templates. I came across this example

#include <iostream>
using namespace std;

//Output function to output any type without type specifiers like printf() family
template <typename T, typename ...P>
void output(T t, P ...p)
{
  cout << t << ' ';
  if (sizeof...(p)) { output(p...); }
  else { cout << '\n'; }
}

//Since variadic templates are recursive, must have a base case
void output() { cout << "\n"; }

//Test it
int main()
{
  //output();
  output('5', 2);

  return(0);
}

However when I attempt to run it I get the error

main.cpp: In instantiation of 'void output(T, P ...) [with T = int; P = {}]':
main.cpp:10:29:   required from 'void output(T, P ...) [with T = char; P = {int}]'
main.cpp:21:16:   required from here
main.cpp:10:29: error: no matching function for call to 'output()'
   if (sizeof...(p)) { output(p...); }
                             ^
main.cpp:7:6: note: candidate: template<class T, class ... P> void output(T, P ...)
 void output(T t, P ...p)
      ^
main.cpp:7:6: note:   template argument deduction/substitution failed:
main.cpp:10:29: note:   candidate expects at least 1 argument, 0 provided
   if (sizeof...(p)) { output(p...); }
                             ^

Any suggestions on how I can fix it. Thanks

MistyD
  • 16,373
  • 40
  • 138
  • 240

3 Answers3

5

Switch the order of declaration of your output functions:

//Since variadic templates are recursive, must have a base case
void output() { cout << "\n"; }

//Output function to output any type without type specifiers like printf() family
template <typename T, typename ...P>
void output(T t, P ...p)
{
  cout << t << ' ';
  if (sizeof...(p)) { output(p...); }
  else { cout << '\n'; }
}

The overload resolution rules get funky for template functions, so the compiler only considered the overloads that had been declared so far, and didn't consider your non-template. When writing several overloads, where one or more of them are template functions, declaration order matters.

Alexander Kondratskiy
  • 4,156
  • 2
  • 30
  • 51
2

It's more complicated than just declaration order. See this answer: Switch passed type from template

template <typename T, typename ...P>
void output(T t, P ...p)
{
  cout << t << ' ';
  if (sizeof...(p)) { output(p...); } // HERE
  else { cout << '\n'; }
}

Regardless of whether or not the function is actually called it must exist. You can't have a branch within a function that though never executed results in a syntax error. Your check for sizeof...(p) is thus kinda pointless.

So to make your code clearer, in that it expresses itself completely and doesn't have extra stuff, change to this:

void output() { cout << "\n"; }

//Output function to output any type without type specifiers like printf() family
template <typename T, typename ...P>
void output(T t, P ...p)
{
  cout << t << ' ';
  output(p...);
}

You also don't have to define them in that order, though it's easier. You could do this:

void output();

//Output function to output any type without type specifiers like printf() family
template <typename T, typename ...P>
void output(T t, P ...p)
{
  cout << t << ' ';
  output(p...);
}

void output() { cout << "\n"; }

Or even this:

template <typename T, typename ...P>
void output(T t, P ...p)
{
  void output();
  cout << t << ' ';
  output(p...);
}

void output() { cout << "\n"; }

Edit: Actually, you can avoid the need for a 0 parameter version:

template < typename T >
void output(T t) { cout << t << "\n"; }

//Output function to output any type without type specifiers like printf() family
template <typename T, typename ...P>
void output(T t, P ...p)
{
  cout << t << ' ';
  output(p...);
}
Community
  • 1
  • 1
Edward Strange
  • 40,307
  • 7
  • 73
  • 125
1

T t will be the first argument and P ...p the rest.

output('5', 2);
//          ^ 

You put the function void output() below, and it's not visible.

To fix this, you can declare the function above so that it will be seen:

void output();

Or move the function with no parameters above the other one:

void output() { cout << "\n"; }

template <typename T, typename ...P>
void output(T t, P ...p) { // ... }
Andreas DM
  • 10,685
  • 6
  • 35
  • 62