如何编写可变参数模板的递归函数?

时间:2016-08-04 19:21:50

标签: c++ c++11 recursion variadic-templates variadic-functions

我正在尝试编写一个可变参数模板constexpr函数,该函数计算给定模板参数的总和。这是我的代码:

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;
}

不幸的是,在尝试解析error C2668: 'f': ambiguous call to overloaded function来电时,它不会编译报告错误消息f<3,>()

我还尝试将我的递归基础案例更改为接受0模板参数而不是1:

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

但是这段代码也没有编译(消息error C2912: explicit specialization 'int f(void)' is not a specialization of a function template)。

我可以提取第一个和第二个模板参数来进行编译和工作,如下所示:

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

但这似乎不是最好的选择。所以,问题是:如何以优雅的方式编写这个计算?

UP :我也尝试将其写为单个函数:

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

这也不起作用:error C2672: 'f': no matching overloaded function found

6 个答案:

答案 0 :(得分:18)

你的基础案例错了。您需要一个空列表的大小写,但正如编译器所建议的那样,您的第二次尝试不是有效的模板专业化。为零参数定义有效实例化的一种方法是创建一个接受空列表的重载

Int

编辑:为了完整起见也是我的第一个答案,@ alexeykuzmin0通过添加条件修复:

List(-1,-1,-1,-1,-1)

答案 1 :(得分:10)

我发现将代码从模板参数移动到函数参数通常更容易:

constexpr int sum() { return 0; }

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

如果你真的希望它们作为模板参数,你可以让你的f通过向下移动来sum

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

这是constexpr,所以只需使用int即可。

答案 2 :(得分:10)

<nav>
  <ul>
    <li><a>A</a></li>
    <li class="selected">
      <a>
        B
      </a>
    </li>
    <li><a>C</a></li>
  </ul>
</nav>

您收到此错误:

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;
}

这是因为可变参数包可以被赋予0个参数,因此error C2668: 'f': ambiguous call to overloaded function while trying to resolve f<3,>() call. 可以与f<3>一起使用&#34;扩展&#34;到template<int First, int... Rest>。但是,您还具有template<3, >的特化,因此编译器不知道选择哪一个。

明确说明第一个和第二个模板参数是解决此问题的完全有效且良好的解决方案。

当您尝试将基本案例更改为:

template<int First>

你遇到了问题,因为功能不能以这种方式专门化。类和结构可以是,但不是函数。

解决方案#1:带有template <> constexpr int f() { return 0; }

的C ++ 17倍表达式
constexpr

解决方案#2:使用template <typename... Is> constexpr int sum(Is... values) { return (0 + ... + values); } 函数

constexpr

解决方案#3:使用模板元编程

constexpr int sum() {
    return 0;
}

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

答案 3 :(得分:7)

按照通常的方式总结一下。

template<int... Args>
constexpr int f() {
   int sum = 0;
   for(int i : { Args... }) sum += i;
   return sum;
}

答案 4 :(得分:2)

使用std::initializer_list的更通用的解决方案是:

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;
}

答案 5 :(得分:1)

这与@ T.C上面提到的非常相似:

#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;
}