How to write a recursive function of a variation pattern?

I am trying to write a variation pattern constexpr that calculates the sum of the given template parameters. Here is my code:

 template<int First, int... Rest> constexpr int f() { return First + f<Rest...>(); } template<int First> constexpr int f() { return First; } int main() { f<1, 2, 3>(); return 0; } 

Unfortunately, it does not compile error message error C2668: 'f': ambiguous call to overloaded function when trying to resolve the call f<3,>() .

I also tried changing the underlying recursion case to accept 0 template arguments instead of 1:

 template<> constexpr int f() { return 0; } 

But this code also does not compile ( error C2912: explicit specialization 'int f(void)' is not a specialization of a function template message error C2912: explicit specialization 'int f(void)' is not a specialization of a function template ).

I could extract the first and second arguments of the template for compilation and work, for example:

 template<int First, int Second, int... Rest> constexpr int f() { return First + f<Second, Rest...>(); } 

But this is not the best option. So the question is: how to write this calculation in an elegant way?

UP: I also tried to write this as one function:

 template<int First, int... Rest> constexpr int f() { return sizeof...(Rest) == 0 ? First : (First + f<Rest...>()); } 

And this also does not work: error C2672: 'f': no matching overloaded function found .

+6
source share
6 answers

Your base case was wrong. You need a case for an empty list, but as the compiler points out, your second attempt was not a valid specialization. One way to determine a valid instance for null arguments is to create an overload that accepts an empty list

 template<class none = void> constexpr int f() { return 0; } template<int First, int... Rest> constexpr int f() { return First + f<Rest...>(); } int main() { f<1, 2, 3>(); return 0; } 

EDIT: for the sake of completeness of my first answer, that @ alexeykuzmin0 is fixed by adding a conditional:

 template<int First=0, int... Rest> constexpr int f() { return sizeof...(Rest)==0 ? First : First + f<Rest...>(); } 
+12
source

I find it easiest to move code from template arguments to function arguments:

 constexpr int sum() { return 0; } template <class T, class... Ts> constexpr int sum(T value, Ts... rest) { return value + sum(rest...); } 

If you really want them to be template arguments, you can just f call f by moving them down:

 template <int... Is> constexpr int f() { return sum(Is...); } 

This is constexpr , so just using int fine.

+8
source

Just summarize in the usual way.

 template<int... Args> constexpr int f() { int sum = 0; for(int i : { Args... }) sum += i; return sum; } 
+6
source
 template<int First, int... Rest> constexpr int f() { return First + f<Rest...>(); } template<int First> constexpr int f() { return First; } int main() { f<1, 2, 3>(); return 0; } 

You will get this error:

 error C2668: 'f': ambiguous call to overloaded function while trying to resolve f<3,>() call. 

This is due to the fact that 0 arguments can be given to a variable parameter package, therefore f<3> can work with template<int First, int... Rest> , "expanding" to template<3, > . However, you also have a specialization of template<int First> , so the compiler does not know which one to choose.

Explicitly specifying the first and second arguments to the pattern is a completely correct and good solution to this problem.


When trying to change the base case to:

 template<> constexpr int f() { return 0; } 

You have a problem because functions cannot be specialized in this way. Classes and structures can be, but not functions.


Solution # 1: (with C ++ 17, so this is only useful in the future)

 template<class... Is> constexpr int sum(Is... values) { return (0 + ... + values); } 

Decision number 2. Take arguments as function arguments instead of template arguments

 constexpr int sum() { return 0; } template<class I, class... Is> constexpr int sum(I first, Is... rest) { return first + sum(rest...); } 

Solution # 3: Use structs

(it will almost certainly be written cleaner)

 template<int... Ts> struct int_pack; template<class> struct sum_impl; template<> struct sum_impl<int_pack<>> { enum { value = 0 }; }; template<int First, int... Rest> struct sum_impl<int_pack<First, Rest...>> { enum { value = First + sum_impl<int_pack<Rest...>>::value }; }; template<int... Is> struct sum : public sum_impl<int_pack<Is...>> {}; 
+6
source

A more general solution using std::initializer_list would be:

 template <typename... V> auto sum_all(V... v) { using rettype = typename std::common_type_t<V...>; rettype result{}; (void)std::initializer_list<int>{(result += v, 0)...}; return result; } 
0
source

This is very similar to what @TC suggested:

 #include<iostream> #include<numeric> template<int First, int... Rest> constexpr int f() { auto types = {Rest...}; return std::accumulate(std::begin(types), std::end(types),0); } int main() { std::cout <<f<1, 2, 3>(); return 0; } 
0
source

All Articles