Consider following code, I don't understand why empty function of print must be defined.
#include <iostream>
using namespace std;
void print()
{
}
template <typename T, typename... Types>
void print (const T& firstArg, const Types&... args)
{
cout << firstArg << endl; // print first argument
print(args...); // call print() for remaining arguments
}
int main()
{
int i=1;
int j=2;
char c = 'T';
print(i,"hello",j,i,c,"word");
}
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.
A variadic template is a class or function template that supports an arbitrary number of arguments. This mechanism is especially useful to C++ library developers: You can apply it to both class templates and function templates, and thereby provide a wide range of type-safe and non-trivial functionality and flexibility.
Variadic functions are functions (e.g. std::printf) which take a variable number of arguments. To declare a variadic function, an ellipsis appears after the list of parameters, e.g. int printf(const char* format...);, which may be preceded by an optional comma.
Function arguments are placed on the stack. So executing sum_squares(2,3) means placing 2 and 3 (amongst some other things) on the stack. Putting these two values on the stack means advancing the stack pointer enough for two int values, placing our two values in the free space.
Recursion is the most general way to program variadic templates, but it's far from the only way. For simple use cases like this, doing a pack expansion directly within a braced initializer list is shorter and likely faster to compile.
template <typename... Types>
void print (const Types&... args)
{
using expander = int[];
(void) expander { 0, (void(cout << args << endl), 0) ...};
}
In C++17, we'll be able to use a fold expression:
template <typename... Types>
void print (const Types&... args)
{
(void(cout << args << endl) , ...);
}
CORRECT WAY:
Variadic templates is strictly related to induction
, a mathematical concept.
The compiler resolves the following function call
print('a', 3, 4.0f);
into
std::cout<< 'a' <<std::endl;
print(3, 4.0f);
which is resolved into
std::cout<< 'a' <<std::endl;
std::cout<< 3 <<std::endl;
print( 4.0f);
which is resolved into
std::cout<< 'a' <<std::endl;
std::cout<< 3 <<std::endl;
std::cout<< 4.0f <<std::endl;
print();
At this point it searches for a function overload whose match is the empty function.
The culprit is that you must have, for every possible combination of parameters, only 1 function.
ERROR 1:
Doing the following would be an error
template< typename T>
void print( const T& arg) // first version
{
cout<< arg<<endl;
}
template <typename T, typename... Types>
void print (const T& firstArg, const Types&... args) // second version
{
cout << firstArg << endl; // print first argument
print(args...); // call print() for remaining arguments
}
Because when you call print
the compiler doesn't know which function to call.
Does print(3)
refers to "first" or "second" version? Both would be valid because the first has 1 parameter, and the second can accept one parameter too.
print(3); // error, ambiguous, which one you want to call, the 1st or the 2nd?
ERROR 2:
The following would be an error anyway
// No empty function
template <typename T, typename... Types>
void print (const T& firstArg, const Types&... args)
{
cout << firstArg << endl; // print first argument
print(args...); // call print() for remaining arguments
}
In fact, if you use it alone without the compiler would do
print('k', 0, 6.5);
which is resolved into
std::cout<<'k'<<std::endl;
print(0, 6.5);
which is resolved into
std::cout<<'k'<<std::endl;
std::cout<< 0 <<std::endl;
print( 6.5);
which is resolved into
std::cout<<'k'<<std::endl;
std::cout<< 0 <<std::endl;
std::cout<< 6.5 <<std::endl;
print(); //Oops error, no function 'print' to call with no arguments
As you see in the last attempt, the compiler tries to call print()
with no arguments. However if such a function does not exists it is not called, and that's why you should provide that empty function (don't worry, the compiler will optimize code so empty functions don't decrease performance).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With