Home C++ Introduction Decisions Loops Input/Output Functions Stack and Heap References Arrays Searching and Sorting Recursion Pointers Character and Strings Structures Classes Inheritance Exceptions Templatess STL Modern C++ Misc Books ----

Variadic Templates


Contents

Introduction

We could pass an unknown number of arguments in C++ using the "va_list" approach. The C++ 11 standard gives us variadic templates. It uses a template approach.

Printf Example




File: var1.cpp
#include <iostream>
using namespace std ;


template<typename T>
void printf1(T v)
{
  cout << "Single argument function:" << v << endl ;

}

//template<typename T>
/*
void printf1()
{
  cout << "Empty printf." << endl ;

}
*/

template<typename T, typename... Args>
void printf1(T first, Args... args)
{
  cout << "first:" << first << endl ;

 // printf1( first ) ;
  //cout << "Enter:" << endl ;
  printf1(  args...  )  ;
}

int main()
{

    printf1( 1, 2, 3 );
    //printf1( "1", 2, 3 );
    cout << "End of main." << endl ;



}
Output:
$ rm a.exe ; g++ var1.cpp ; ./a.exe
first:1
first:2
Single argument function:3
End of main.

We define a function:

template
void printf1(T first, Args... args)
{

We have to use the word "typename" and use "..." in the
argument to signify any number of arguments.
We call it like:

printf1( 1, 2, 3 );

When the template function is called then the arguments
are split up as:

    first=1  args= 2,3

We print the first argument and then recursively call the same
function name.

 printf1(  args...  )  ;

 This again splits the arguments into

   first=2   args= 3

 We print the 2 and then we call the "printf1" with
 3. However now the printf1 only has a single argument
 so it calls the overloaded function that we defined earlier
 that only takes a single argument. What if we did not
 have the single defined function.




File: var2.cpp
#include <iostream>
using namespace std ;

/*
template<typename T>
void printf1(T v)
{
  cout << "Single argument function:" << v << endl ;

}
*/

//template<typename T>
/*
void printf1()
{
  cout << "Empty printf." << endl ;

}
*/

template<typename T, typename... Args>
void printf1(T first, Args... args)
{
  cout << "first:" << first << endl ;

 // printf1( first ) ;
  //cout << "Enter:" << endl ;
  printf1(  args...  )  ; //LABEL A
}

int main()
{

    printf1( 1, 2, 3 );
    //printf1( "1", 2, 3 );
    cout << "End of main." << endl ;



}
$ rm a.exe ; g++ var2.cpp ; ./a.exe
var2.cpp: In instantiation of ‘void printf1(T, Args ...) [with T = int; Args = {}]’:
var2.cpp:29:10:   recursively required from ‘void printf1(T, Args ...) [with T = int; Args = {int}]’
var2.cpp:29:10:   required from ‘void printf1(T, Args ...) [with T = int; Args = {int, int}]’
var2.cpp:35:9:   required from here
var2.cpp:29:10: error: no matching function for call to ‘printf1()’
   29 |   printf1(  args...  )  ;
      |   ~~~~~~~^~~~~~~~~~~~~
var2.cpp:23:6: note: candidate: ‘template void printf1(T, Args ...)’
   23 | void printf1(T first, Args... args)
      |      ^~~~~~~
var2.cpp:23:6: note:   template argument deduction/substitution failed:
var2.cpp:29:10: note:   candidate expects at least 1 argument, 0 provided
   29 |   printf1(  args...  )  ;
      |   ~~~~~~~^~~~~~~~~~~~~
bash: ./a.exe: No such file or directory

We received a compiler error.
We had the arguments 1,2 and 3.
This got split up as:

 1   2,3
and then
 2    3
and then say we printed out 2 and then we called
"printf1" at "LABEL A"

  printf 3

Now the "first" is 3 and the args is empty.
We print out 3 and then call the "printf1" at
"LABEL A" with an empty args. However we only
have 1 "printf1" function and that expects
at least 1 argument that is assigned to "first".

The compiler error states:

var2.cpp:23:6: note:   template argument deduction/substitution failed:
var2.cpp:29:10: note:   candidate expects at least 1 argument, 0 provided
   29 |   printf1(  args...  )  ;


Ok why don't we define an empty "printf1" function and see if that
resolves the issue.


File: var3.cpp
#include <iostream>
using namespace std ;

/*
template<typename T>
void printf1(T v)
{
  cout << "Single argument function:" << v << endl ;

}
*/

//template<typename T>

void printf1()
{
  cout << "Empty printf." << endl ;

}


template<typename T, typename... Args>
void printf1(T first, Args... args)
{
  cout << "first:" << first << endl ;

 // printf1( first ) ;
  //cout << "Enter:" << endl ;
  printf1(  args...  )  ; //LABEL A
}

int main()
{

    printf1( 1, 2, 3 );
    //printf1( "1", 2, 3 );
    cout << "End of main." << endl ;



}
Output:
$ rm a.exe ; g++ var3.cpp ; ./a.exe
rm: cannot remove 'a.exe': No such file or directory
first:1
first:2
first:3
Empty printf.
End of main.


This resolves the issue and we see that the last call
to the "printf1" went to the empty function.
Notice we did not make the "printf" function a template
function. Since we are not taking any arguments; how is the
system going to determine the type for the template ? We will
get a copmpiler error with the statement:

template
void printf1()
{
  cout << "Empty printf." << endl ;

}




File: var4.cpp
#include <iostream>
using namespace std ;


template<typename T>
void printf1(T v)
{
  cout << "Single argument function:" << v << endl ;

}


//template<typename T>
/*
void printf1()
{
  cout << "Empty printf." << endl ;

}
*/

template<typename T, typename... Args>
void printf1(T first, Args... args)
{
  cout << "first:" << first << endl ;

 // printf1( first ) ;
  //cout << "Enter:" << endl ;
  printf1(  args...  )  ; //LABEL A
}

int main()
{

    //printf1( 1, 2, 3 );
    printf1( "One", 2, 3 );
    cout << "End of main." << endl ;



}
$ rm a.exe ; g++ var4.cpp ; ./a.exe
first:One
first:2
first:3
Empty printf.
End of main.


The program "var4.cpp" shows that our program works
for arguments that are of different types as well.

Exercises



Fill in the code for the "adder" functions.
File: var_ex1.cpp

Solutions




File: var_soln1.cpp