C++ variadic template sum

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

  1. add an overload which does take zero arguments, like

    int adder() { return 0; }
    
  2. 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


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;
}

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

Tags:

C++

C++11