How to sum variadic arguments passed in to a variadic macro?

Don't use a variadic macro. Visual C++ 14 (or 2015) is a C++11/14 compliant compiler. That means it it supports variadic templates. You can easily recurse a parameter pack to get the sum of the parameters and getting the count can be done by using sizeof.... This lets you write count as

template<typename... Args>
auto count(Args&&...)
{
    return sizeof...(Args);
}

and then sum can be written as

// base case
template<typename T>
auto sum(T&& first)
{
    return first;
}

// multiple parameters case
template<typename T, typename... Args>
auto sum(T&& first, Args&&... rest)
{
    return first + sum(rest...);
}

using those in

int main()
{
    std::cout << count(3,4,5) << "\n";
    std::cout << sum(3,4,5);
}

prints

3
12

which you can see in this live example.


As suggested by HolyBlackCat you can use the dummy array trick to avoid using recursion. That would give you a sum that looks like

template <typename ...P> 
auto sum(const P &... params)
{
    using dummy_array = int[];
    std::common_type_t<P...> ret{}; // common_type_t is to find the appropriate type all of the parameter can be added to
    (void)dummy_array{(void(ret += params), 0)..., 0}; // add the parameter to ret, discard it's result, return the value of 0 for the array element
    return ret;
}

Do note though that this might not work correctly for all types, like shown here with std::valarray. Changing it to

template <typename T, typename ...P> 
auto sum(T first, P&&... rest) // copy the first parameter to use it as the accumulator
{
    using dummy_array = int[];
    (void)dummy_array{(void(first += params), 0)..., 0}; // add the parameter to ret, discard it's result, return the value of 0 for the array element
    return first;
}

should be more correct, although it could probably be improved some more (suggestions/edits welcomed)


If you can use a C++17 complaint compiler then sum can be even further simplified using a fold expression like

template<typename... Args>
auto sum(Args&&... rest)
{
    return (rest + ...);
}

Adding onto @NathanOliver, if you'd like to use variadic templates without recursion, std::initializer_list and std::common_type are both available in C++11, allowing you to do this instead:

template <typename... Args,                                      
          typename T = typename std::common_type<Args...>::type> 
T sum(Args&&... args) {                                          
    std::initializer_list<T> l{args...};                         
    return std::accumulate(l.begin(), l.end(), T{});             
}                                                                

Edit: Note that this solution will allow sum to be invoked on all types that are implicitly convertible to a common type (that is what common_type does).

For example:

sum(1, 2, 3)    // Ok, all ints
sum(1, 2, true) // Ok, bool converts to int
sum(1, 2, 3.)   // Error, int to double is a narrowing conversion

Tags:

C++