How to write a variadic template recursive function?

后端 未结 6 1197
走了就别回头了
走了就别回头了 2020-12-09 03:39

I\'m trying to write a variadic template constexpr function which calculates sum of the template parameters given. Here\'s my code:

template<         


        
相关标签:
6条回答
  • 2020-12-09 04:16

    Just sum it up the usual way.

    template<int... Args>
    constexpr int f() {
       int sum = 0;
       for(int i : { Args... }) sum += i;
       return sum;
    }
    
    0 讨论(0)
  • 2020-12-09 04:27

    Your base case was wrong. You need a case for the empty list, but as the compiler suggests, your second try was not a valid template specialization. One way to define a valid instantiation for zero 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 completeness sake also my first answer, that @alexeykuzmin0 fixed by adding the conditional:

    template<int First=0, int... Rest>
    constexpr int f()
    {
        return sizeof...(Rest)==0 ? First : First + f<Rest...>();
    }
    
    0 讨论(0)
  • 2020-12-09 04:35

    It is quite similar to what @T.C suggested above:

    #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 讨论(0)
  • 2020-12-09 04:36

    I find it generally easier to move the 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 as template arguments, you can have your f just call sum by moving them down:

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

    It's constexpr, so just using ints is fine.

    0 讨论(0)
  • 2020-12-09 04:38
    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 get this error:

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

    This is because a variadic parameter pack can be given 0 arguments, so f<3> could work with template<int First, int... Rest> by "expanding" to template<3, >. However, you also have the specialization of template<int First>, so the compiler does not know which one to choose.

    Explicitly stating the first and second template arguments is a completely valid and good solution to this problem.


    When you try to change the base case to:

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

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


    Solution #1: C++17 fold expression with constexpr

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

    Solution #2: Use a constexpr function

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

    Solution #3: Use Template Metaprogramming

    template <int... Is>
    struct sum;
    
    template <>
    struct sum<>
        : std::integral_constant<int, 0>
    {};
    
    template <int First, int... Rest>
    struct sum<First, Rest...>
        : std::integral_constant<int,
            First + sum_impl<Rest...>::value>
    {};
    
    0 讨论(0)
  • 2020-12-09 04:38

    A more generic 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 讨论(0)
提交回复
热议问题