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

ale*_*in0 15 c++ recursion variadic-functions variadic-templates c++11

我正在尝试编写一个可变参数模板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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

template<>
constexpr int f()
{
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是这段代码也没有编译(消息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...>();
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

这也行不通:error C2672: 'f': no matching overloaded function found.

exa*_*ple 19

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 我今天学到了新东西; 谢谢. (2认同)

Bar*_*rry 10

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

constexpr int sum() { return 0; }

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

如果你真的想要它们作为模板参数,你可以通过向下移动来f调用sum它们:

template <int... Is>
constexpr int f() {
    return sum(Is...);
}
Run Code Online (Sandbox Code Playgroud)

是的constexpr,所以只使用ints很好.


Jus*_*tin 10

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;
}
Run Code Online (Sandbox Code Playgroud)

你收到这个错误:

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

这是因为可变参数包可以被赋予0个参数,因此f<3>可以template<int First, int... Rest>通过"扩展"来实现template<3, >.但是,你也有专门化template<int First>,因此编译器不知道选择哪一个.

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


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

template <>
constexpr int f()
{
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

你有一个问题,因为函数不能以这种方式专门化.类和结构可以是,但不是函数.


解决方案#1:C++ 17倍表达式 constexpr

template <typename... Is>
constexpr int sum(Is... values) {
    return (0 + ... + values);
}
Run Code Online (Sandbox Code Playgroud)

解决方案#2:使用constexpr功能

constexpr int sum() {
    return 0;
}

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

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

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>
{};
Run Code Online (Sandbox Code Playgroud)


T.C*_*.C. 7

通过常规方式总结一下.

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