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
}
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));
}
``
The function need additional check:
#include <type_traits>
template <typename T>
T sum(T v)
{
static_assert(std::is_arithmetic<std::remove_reference<decltype(v)>::type>::value,
"type should be arithmetic");
return v;
}
and it's better pass by value.
otherwise we can get strange result:
int main() {
std::cout << sum(1,"Hello World");
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With