Search code examples
c++templatessumvariadic-templatesdecltype

variadic templates sum operation left associative


The code below works for the: goal for the left associative sum operation: sum(1,2,3,4);

However, it won't work correctly for sum(1,2,3,4,5) or sum(1,2,3,4,5,...). Anything with more than 4 arguments gives the error:

error: no matching function for call to sum(int, int, int, int, int)

=================================

template <typename T>
T sum(const T& v) {
return v;
}

template <typename T1, typename T2>
auto sum(const T1& v1, const T2& v2) -> decltype( v1 + v2) {
return v1 + v2;
}

template <typename T1, typename T2, typename... Ts>
auto sum(const T1& v1, const T2& v2, const Ts&... rest) -> decltype( v1 + v2 +      sum(rest...) ) {
return v1 + v2 + sum(rest... );
}

int main() {
    cout << sum(1,2,3,4); //works correctly
    //cout << sum(1,2,3,4,5); //compile error

}

Solution

  • That seems to be a bug in GCC, when working with variadic templates, auto return types and recursive reference to the same variadic template in the trailing return type.

    C++11 - only right associative

    It is solvable, through good old template meta programming:

    //first a metafunction to calculate the result type of sum(Ts...)
    template <typename...> struct SumTs;
    template <typename T1> struct SumTs<T1> { typedef T1 type; };
    template <typename T1, typename... Ts>
    struct SumTs<T1, Ts...>
    {
      typedef typename SumTs<Ts...>::type rhs_t;
      typedef decltype(std::declval<T1>() + std::declval<rhs_t>()) type;
    };
    
    //now the sum function
    template <typename T>
    T sum(const T& v) {
      return v;
    }
    
    template <typename T1, typename... Ts>
    auto sum(const T1& v1, const Ts&... rest) 
      -> typename SumTs<T1,Ts...>::type //instead of the decltype
    {
      return v1 + sum(rest... );
    }
    
    #include <iostream>
    using std::cout;
    
    int main() {
      cout << sum(1,2,3,4,5);    
    }
    

    PS: to be even more generic, the whole thing could be pimped with "universal references" and std::forward.

    C++17 fold expressions

    In C++17, the problem can be solved in basically one line:

    template<typename T, typename... Ts>
    constexpr auto sum(T&& t, Ts&&... ts) 
    {
      return (std::forward<T>(t) + ... + std::forward<Ts>(ts));
    }
    ``