I have seen these questions:
but they do not consider variadic functions and variadic template .
Consider the problem of writing a function to find the average of either 2 or 3 positive numbers .
There are four ways we could implement this function:
Using a default parameter:
int average(int x , int y , int z=-1);
Using function overloads:
int average(int x , int y);
int average(int x , int y , int z);
Using a variadic function
int average(int x,int y, ...);
Using a variadic template
int sum=0;
int count=0;
void average(){
return;
}
template <class A, class ...B> void average(A argHead, B... argTail){
sum =sum+argHead;
count+=1;
average(argTail...);
}
What are the pros and cons of using one method over another ?
Given the constraints, namely average of either two or three positive numbers, the second option is the best.
The first option, with the optional argument, requires a test for positivity:
int average(int x,int y,int z=-1) {
if (z<0) return (x+y)/2;
else return (x+y+z)/3;
}
In term of providing the service of finding an average, that test is unnecessary. Further, it is brittle: it's reasonable to consider finding the average of numbers that could also be negative, and this solution is not compatible with that case.
The third option, the variadic function, is inappropriate because you will need to provide information in the call to indicate how many arguments are present, e.g. with a terminating negative number, or with the first parameter indicating the number of remaining arguments. This complicates the interface. Further, it is not type safe, and the burden then rests on the users of the function to be scrupulous in calling it only with ints — the compiler will not prohibit or warn about incorrect usage.
Using a variadic template is a good, general solution that extends easily to any number of parameters. Here though, we need only the two and three argument cases, and so it's overly complex for the application. If you were to use this though, you'd want to use a different implementation that did not require global variables. The following uses a namespace to hide some of the implementation details, and forces the use of int arguments by fixing the head type before the recursion:
namespace impl {
int sum() { return 0; }
template <typename... V>
int sum(int a,V... rest) { return a+sum(rest...); }
}
template <typename... V>
int average(int a,V... rest) {
return impl::sum(a,rest...)/(1+sizeof...(V));
}
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