【问题标题】:How can I do a recursion on C++ variadic templates?如何对 C++ 可变参数模板进行递归?
【发布时间】:2020-11-17 14:23:33
【问题描述】:

假设我想定义一个 C++ 函数,它在括号内没有输入参数,但在 <> 参数内。我有一个参数包作为输入参数。这意味着我必须编写例如一个函数

int calculate<args...>()
{
   return 1 + calculate<some_arg,args...>();
}

还有一个我必须给出的基本案例实现。但是,我遇到了很多编译器错误,因为我不知道如何正确编写这种形式的递归。在上面的函数声明之前我要写什么?

template<int... args>

(如果 args 的数据类型是 int;任何其他数据类型也可以采用类似的方式)?或者我必须写什么来避免编译器错误?我也试过了

template<int some_arg,int... args>

但我也不知道如何处理可变参数模板(如何解压它们)。有什么帮助吗?

编辑:

我对一种特殊情况的尝试

template<bool... dg>
int calculate<0>()
{
    return 1;
}

错误信息是:

错误:“

【问题讨论】:

  • 请创建minimal reproducible example 并粘贴编译器错误消息。
  • 如果你搜索的话,你应该可以在 SO 上找到很多这样的例子。
  • 函数模板可能没有部分特化。

标签: c++ templates variadic-templates


【解决方案1】:

但我也不知道如何处理可变参数模板(如何解压它们)。有什么帮助吗?

从 C++17 开始,您无需使用递归,但可以使用包扩展:

#include <iostream>

template<int ...Args>
constexpr int calculate() {
   return (Args + ...);
}

int main() {
    std::cout << calculate<1, 2, 3>();  // 6
}

如果您想允许其他类型的非类型模板参数,您可以为非类型模板参数使用占位符类型 (auto),这也是 C++17 的一个特性:

template<auto ...Args>
constexpr auto calculate() {
   return (Args + ...);
}

由于你不能部分特化函数模板,如果你想为不同的特化提供不同的实现,你将不得不使用对类模板的委托:

#include <iostream>
#include <ios>

template<auto ...Args>
struct calculate_impl {
    static constexpr auto calc() { return (Args + ...); }
};

template<bool ...Args>
struct calculate_impl<Args...> {
    static constexpr bool calc() { return (Args && ...); }
};

template<auto ...Args>
constexpr auto calculate() {
   return calculate_impl<Args...>::calc();
}

int main() {
    std::cout << calculate<1, 2, 3>();  // 6
    std::cout << std::boolalpha 
        << "\n" << calculate<false,true>()  // false
        << "\n" << calculate<true, true>();  // true
}

【讨论】:

    【解决方案2】:

    如果您使用的是 C++17+:请参阅 dfrib 的回答

    以下是您将如何实现一个函数以使用模板递归添加参数包的元素

    template<int arg>
    constexpr int add()
    {
        return arg;
    }
    
    template<int arg1, int arg2, int... args>
    constexpr int add()
    {
        return arg1 + add<arg2, args...>();
    }
    

    关于是否要创建特殊情况

    template<int arg>
    constexpr int calculate()
    {
        return arg;
    }
    
    template<> int calculate<0>() { return 1; } // special case
    
    template<int arg1, int arg2, int... args>
    constexpr int calculate()
    {
        return calculate<arg1>() + calculate<arg2,args...>();
    }
    

    这将使它每次在你的参数列表中有一个零时,它会添加 1 而不是 0

    【讨论】:

    • 我认为你不需要arg2
    • 我也是这么想的,但是当我尝试不使用它时,得到了error: call to 'calculate' is ambiguous
    【解决方案3】:

    另一种递归方式允许您在单个函数中使用sizeof... 运算符:

    template<int lhs, int... rhs>
    int add()
    {
        if constexpr(sizeof...(rhs))
        {
            return lhs + add<rhs...>();
        }
        else
        {
            return lhs;
        }
    }
    

    if constexpr 也需要 C++17,但它可以做折叠表达式可能无法做到的事情。

    【讨论】:

      猜你喜欢
      • 2017-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-18
      相关资源
      最近更新 更多