C++可变参数模板总和

Raf*_*ssi 6 c++ c++11

我试图理解名为"variadic"的C++ 11特性.看看这个简单的代码:

#include <iostream>

using namespace std;

template<typename T, typename... Args>
T adder(T first, Args... args) {
  return first + adder(args...);
}

int main() {

  int c = adder(1,8,4);
  cout << c << endl;

  cout << "Hello World!" << endl;
  return 0;

}
Run Code Online (Sandbox Code Playgroud)

阅读c ++入门书籍我已经理解它们以递归方式工作,我也看到在递归调用中,args...部分被传递.

我正在使用MinGW 5和QtCreator来测试它.看

在此输入图像描述

好的,要修复我可以调用的太少的参数,adder(first, args...)但现在递归不正确并且程序崩溃了.该怎么办?我无法理解如何做到这一点.


在线查看我发现了一个这样的例子

template <typename T, typename... Rest>
double sum(T t, Rest... rest) {
  return t + sum(rest...);
}
Run Code Online (Sandbox Code Playgroud)

它基本相同.我是否必须使用显式(非模板)返回类型?

Chr*_*n G 10

您需要"stop-recursion-case"(现在不知道正确的名称; UPDATE:它被称为"base-case",感谢Quentin),当模板函数展开时只有一个参数.

#include <iostream>

template<typename T>
T adder(T first) {
  return first;
}

template<typename T, typename... Args>
T adder(T first, Args... args) {
  return first + adder(args...);
}

int main() {
  const int c = adder(1, 8, 4);
  std::cout << c << '\n';
  return 0;
}
Run Code Online (Sandbox Code Playgroud)


Syn*_*nck 8

使用 C++17折叠表达式,您可以使用单个函数来完成。
您不需要“停止递归案例”。

template<typename... Args>
auto sum(Args... args)
{
    return (args + ...);
}
Run Code Online (Sandbox Code Playgroud)

示例用法,全部打印 42

std::cout << sum(42) << '\n';
std::cout << sum(11, 31.0) << '\n'; // different types
std::cout << sum(3, 4, 5, 6, 7, 8, 9) << '\n';

using namespace std::string_literals;
std::cout << sum("4"s, "2"s) << '\n'; // concatenating strings
Run Code Online (Sandbox Code Playgroud)


Use*_*ess 7

你的递归展开如下:

adder(1,8,4)
-> adder<int,int,int>(1,8,4)
-> 1 + adder<int,int>(8,4)
-> 1 + 8 + adder<int>(4)
-> 1 + 8 + 4 + adder<>()
Run Code Online (Sandbox Code Playgroud)

所以Args...每次都变得越来越短,最终是空的.但是你的声明

template<typename T, typename... Args>
T adder(T first, Args... args);
Run Code Online (Sandbox Code Playgroud)

不能用零参数调用,它总是需要至少一个(first).

所以,选项也是

  1. 添加该过载采取零点的参数,如

    int adder() { return 0; }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 添加一个只带一个参数的重载,它不会尝试继续递归:

    template <typename T>
    T adder(T t) { return t; }
    
    Run Code Online (Sandbox Code Playgroud)

任何一个会修正错误,但第二个是好,因为它可以用于任何T,和第一只会增加的东西都是从隐式转换int{0}.

这种模式 - 一般的递归情况加上终止案例来阻止递归 - 在引入可变参数模板之前很常见(我们以前使用类似LISP的递归列表来处理这类事情,这自然会像这样使用递归).

由可变参数模板启用的较新样式利用包扩展来完全避免递归.例如,参见使用的求和示例std::apply