I am trying to understand the C++11 feature called "variadic". Look at this simple code:
#include <iostream>
using namespace std;
template<typename T, typename... Args>
T adder(T first, Args... args) {
return first + adder(args...);
}
int main() {
int c = adder(1,8,4);
cout << c << endl;
cout << "Hello World!" << endl;
return 0;
}
Readig the c++ primer book I have understood that they work in recursive way and I also see that in the recursive call the args...
part is passed.
I am using MinGW 5 and QtCreator to test this. Look
Ok, to fix the Too few arguments I could call adder(first, args...)
but now the recursion is not proper and the program crashes. What to do? I cannot understand how to do this.
Looking online I've found an example like this
template <typename T, typename... Rest>
double sum(T t, Rest... rest) {
return t + sum(rest...);
}
It's basically the same. Do I have to put an explicit (non template) return type?
You need the "stop-recursion-case" (do not know the correct name now; UPDATE: it's called "base-case", thanks Quentin) with just one argument when the template function is unfolding.
#include <iostream>
template<typename T>
T adder(T first) {
return first;
}
template<typename T, typename... Args>
T adder(T first, Args... args) {
return first + adder(args...);
}
int main() {
const int c = adder(1, 8, 4);
std::cout << c << '\n';
return 0;
}
Your recursion unfolds like this:
adder(1,8,4)
-> adder<int,int,int>(1,8,4)
-> 1 + adder<int,int>(8,4)
-> 1 + 8 + adder<int>(4)
-> 1 + 8 + 4 + adder<>()
so Args...
is getting shorter every time, and eventually is empty. But your declaration
template<typename T, typename... Args>
T adder(T first, Args... args);
can't be called with zero arguments, it always needs at least one ( first
).
So, the options are either
add an overload which does take zero arguments, like
int adder() { return 0; }
add an overload taking exactly one argument, which doesn't try to continue the recursion:
template <typename T> T adder(T t) { return t; }
either one will fix the bug, but the second is much better, because it works for any T
, and the first will only add things that are implicitly convertible from int{0}
.
This pattern - the general recursive case plus the terminal case which stops recursion - was common before variadic templates were introduced (we previously used LISP-like recursive lists for this sort of thing, which naturally use recursion like this).
The newer style enabled by variadic templates takes advantage of pack expansion to avoid recursion entirely. See for example the summation example using std::apply
The answer from @christian-g is correct. However, I would like you to notice that this function can be generalised to any return type with auto
keyword.
template<typename T1, typename T2>
auto adder(const T1& t1, const T2& t2) {
return t1 + t2;
}
template<typename T1, typename... T2>
auto adder(const T1& t1, const T2&... t2) {
return t1 + adder(t2...);
}
Now you can use it for different types
auto sum1 = adder(1, 2, 3); // sum1 is int
auto sum2 = adder(1, 2., 3); // sum2 is double
There was an issue I have run against once with concatenating values to the string
result, but it was solved in type deduction from char array to std::string
With C++17 fold expression you can do it with a single function.
You don't need the "stop-recursion-case".
template<typename... Args>
auto sum(Args... args)
{
return (args + ...);
}
Example usage, all print 42
std::cout << sum(42) << '\n';
std::cout << sum(11, 31.0) << '\n'; // different types
std::cout << sum(3, 4, 5, 6, 7, 8, 9) << '\n';
using namespace std::string_literals;
std::cout << sum("4"s, "2"s) << '\n'; // concatenating strings
A variadic template parameter like Args
allows the case with sizeof...(Args)=0
, which returns the number of variadic template arguments. Using C++11 you need to define a case that is valid for no variadic templates given like
template<typename _T>
_T adder(_T first) { return first; }
However considering different types like double and int it is not always meaningful to return _T, mayber you want to change it to auto
or declytpe
.
Additionally this problem is solved in C++17
if you are intreseted.
template<typename ...Args>
int sum(Args&&... args) {
return (args + ... + (1 * 2));
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.