10

So basically, I want to create a function like this:

total_sum(1, 2, 5, 4, 2) // return 14
total_sum(5, 6, 2) // return 13

One way that I can use is ellipsis, something like this:

#include <cstdarg>
int total_sum(int count, ...)
{
    int sum{0};
    va_list list;
    va_start(list, count);
    for (int arg{0}; arg < count; ++arg)
    {
         sum += va_arg(list, int);
    }
    va_end(list);
    return sum;
}

But when I call total_sum(), I have to provide an extra argument. So in the example, I have to call:

total_sum(5, 1, 2, 5, 4, 2);
total_sum(3, 5, 6, 2);

which I don't really like. Also, ellipsis is really prone to error, so I want to stay away from them.

Another way I can think of is using some container:

#include <vector>
int total_sum(std::vector<int> values)
{
    int sum{0};
    for(const auto &i : values)
    {
       sum += i;
    }
    return sum;
}

and I call it like:

total_sum({1, 2, 5, 4, 2});
total_sum({3, 5, 6, 2});

But, I want to not have those curly braces, so what can I do? Is there some C++ feature that allows me to do this?


Some relevant links: restrict variadic template arguments, fold expression, parameter packs and a C++11 "equivalent" of fold expressions

  • You can use macros to provide count to your `total_sum` function. Something like this: https://stackoverflow.com/a/2124433/11613622 – brc-dd Nov 18 '21 at 03:39
  • If there is a maximum of the number of parameters, you can using optional parameters -default value for each parameter-, if you send a value it will use it, otherwise it's default value assigned to. For example the default value is 0 which haven't any influence of the sum operations – alnajm Nov 18 '21 at 03:42
  • you answer you title: https://stackoverflow.com/questions/39659127/restrict-variadic-template-arguments – bolov Nov 18 '21 at 06:35

3 Answers3

17

Use C++17 fold expression:

template<class... Args>
constexpr auto total_sum(const Args&... args) {
  return (args + ... + 0);
}

static_assert(total_sum(1, 2, 5, 4, 2) == 14);
static_assert(total_sum(3, 5, 6, 2) == 16);
康桓瑋
  • 33,481
  • 5
  • 40
  • 90
7

In C++11 and newer you can use template parameter packs to create a recursive implementation, like this:

#include <iostream>

// base function
int total_sum()
{
    return 0;
}

// recursive variadic function
template<typename T, typename... Targs>
int total_sum(T firstValue, Targs... Fargs)
{
    return firstValue + total_sum(Fargs...);
}

int main(int, char **)
{
   int s = total_sum(1, 5, 10, 20);
   std::cout << "sum is:  " << s << std::endl;
   return 0;
}

Running the above program produces this output:

sum is:  36
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
2

All of the code below is is based on this article.


You can also do this kind-of C++11 "equivalent" of fold expressions:

#include <iostream>
#include <algorithm>
#include <utility>
#include <type_traits>
/*
Overload a total_sum() function with no arguments.
so it works when calling total_sum() with no argument.
*/
int total_sum()
{
    return 0;
}

template<typename ... I>
int total_sum(const I &... i)
{
    int result{};
    static_cast<void>(std::initializer_list<int>{(result += i, 0)... });
    return result;
}

int main()
{
    std::cout << total_sum() << '\n';
    std::cout << total_sum(1, 2, 5, 4, 2) << '\n';
    std::cout << total_sum(5, 6, 2) << '\n';
}

See this online!

This one can add anything that can convert to an add-able value:

#include <iostream>
#include <algorithm>
#include <utility>
#include <type_traits>

int total_sum()
{
    return 0;
}

template<typename ... V>
typename std::common_type<V...>::type total_sum(const V &... v)
{
    typename std::common_type<V...>::type result = {};
    static_cast<void>(std::initializer_list<int>{ (result += v, 0)... });
    return result;
}
int main()
{
    std::cout << total_sum() << '\n';
    std::cout << total_sum(5, 6, 2) << '\n';
}

See this online!

The code above can be cleaner using C++14:

#include <iostream>
#include <algorithm>
#include <utility>
#include <type_traits>

int total_sum()
{
    return 0;
}
template<typename ... V>
auto total_sum(const V &... v) {
  std::common_type_t<V...> result = {};
  static_cast<void>(std::initializer_list<int>{ (result += v, 0)... });
  return result;
}
int main()
{
    std::cout << total_sum() << '\n';
    std::cout << total_sum(5, 6, 2) << '\n';
}

See this online!