繁体   English   中英

递归模板说明C ++

[英]Recursive template explanation C++

template<typename... ArgTypes>
int add(ArgTypes... args);

template<typename T, typename... ArgTypes>
int add(T t, ArgTypes... args)
{
    int sum = 0;
    return t + add(args...);
}
template<> int add() {
    return 0;
} 

如何添加更多的运算,例如乘法和减法? template<> int add()是什么意思?

谁能详细解释此递归模板如何工作?

UPD:谢谢你们关于减法,是的,减法不是可交换的,因此它实际上不适用于这种递归模板。

UPD2:添加了呼叫堆栈作为社区的参考 调用堆栈的可变参数模板

这是我的解释尝试。

首先:

template<typename... ArgTypes>
int add(ArgTypes... args);

这是起点。 它说:“存在一个名为add的函数,该函数接受零个或多个通用参数”。 它不包含实现,因此就其本身而言,它等于向编译器保证将存在这种功能。

然后我们有:

template<typename T, typename... ArgTypes>
int add(T t, ArgTypes... args)
{
    int sum = 0; // This line isn't doing anything!
    return t + add(args...);
}

这就是说“这里有一个叫做add的函数,它需要一个或多个通用参数”。 它包括一个实现。 该实现的一部分以除第一个参数(即零个或多个)外的所有参数递归调用add(args...) )。 上面的第一个声明告诉我们这是存在的。

如果args中至少有一个参数,则此递归调用最终将再次调用完全相同的函数。 但是,当args包含零参数时会发生什么呢? 我们需要函数的版本(专业化)来处理这种情况,这是第二个定义未处理的唯一情况。 那是第三个声明出现的地方:

template<> int add() {
    return 0;
} 

这定义了一个名为add的函数,该函数需要争论。

因此,总而言之:

  1. 第二个声明定义了一个包含一个或多个论点的函数
  2. 第三个声明定义一个带有参数的函数
  3. 在一起,这意味着我们有一个函数接受第一个声明所声明的零个或多个参数。

谁能详细解释此递归模板如何工作?

我可以试试。

首先你有

template<typename... ArgTypes>
int add(ArgTypes... args);

这是一个可变参数模板函数声明:您声明存在一个add()函数,该函数接收可变参数(零个或多个)数量的argumens。

注意:您声明但未定义函数。

第二:您声明 定义

template<typename T, typename... ArgTypes>
int add(T t, ArgTypes... args)
{
    int sum = 0;
    return t + add(args...);
}

一个不同的模板函数add() ,它们接收模板类型参数( t )和可变参数模板列表( args... )。

线

int sum = 0;

完全没用,因为声明了一个未使用的变量,但以下行

return t + add(args...);

完成返回t和以下args...之间的和( add(args...) )的工作。

所以当args... (因为更好的匹配)不为空且int add(ArgTypes... args)int add(T t, ArgTypes... args)使用add(args...)递归称为int add(T t, ArgTypes... args) int add(ArgTypes... args)args...是一个空列表时。

但是请记住,已声明但未定义int add(ArgTypes... args)

最后一点是

template<> int add() {
    return 0;
} 

这是在空列表的情况下第一个模板功能的完全专业化的定义(请记住,您不能对模板函数进行部分专业化,但可以对其进行完全专业化)。

离题建议:您不需要第一个模板功能; 您可以用更简单的非模板功能代替它。

您可以按以下方式重写代码

int add()
 { return 0; } 

template <typename T, typename... ArgTypes>
int add(T t, ArgTypes... args)
 { return t + add(args...); }

并按照Jarod42的建议,如果可以使用C ++ 17,则可以完全避免使用模板折叠进行递归

template <typename... ArgTypes>
int add (ArgTypes... args)
 { return (... + args); }

或使用auto返回类型。

如何添加更多的运算,例如乘法和减法?

不知道减法(如何定义可变参数减法?),但是对于乘法,您可以使用类似的东西(但基本情况必须返回1 ,而不是0

int mult ()
 { return 1; } 

template <typename T, typename... ArgTypes>
int mult (T t, ArgTypes... args)
 { return t * mult(args...); }

或使用C ++ 17中的模板折叠,

template <typename... ArgTypes>
int mult (ArgTypes... args)
 { return (... * args); }

这是相当普遍的递归可变参数模板 这个想法是我们使用递归

f(x0, x1, ..., xn) = f(f(x0, x1, ..., xn-1), xn) (1)

在您的样本中

add(x0, x1, ..., xn) = add(x0, x1, ..., xn-1) + xn

可变参数模板提供了创建此类结构的简单有用的方法。

首先,定义模板的一般签名(没有实现,因为我们从不使用一般形式)

template<typename... ArgTypes>
int add(ArgTypes... args);

现在,针对至少具有一个参数的情况专门化模板功能。 我们使用递归提取第一个参数,然后递归调用自身,并将参数数量减少一个。

template<typename T, typename... ArgTypes>
int add(T t, ArgTypes... args)
{
    int sum = 0;
    return t + add(args...); // recursive call without first arg
}

为了停止递归,我们将模板专门化用于空模板参数列表(在最后一步添加0 )。

template<> int add() {
    return 0;
} 

对于乘法,只需将+更改为* ,因为两种情况下通用递归形式(1)都相同,并且将return 0更改为return 1 (在最后一步乘以1 )。

在减法情况下,递归(1)的一般形式不可用,因为ab != ba ,会引起歧义。 还可以进行除法和其他非交换运算。 您将必须澄清操作顺序。

递归有一个基本情况 因此,您可以将template<> int add()作为涵盖T的整数的基本情况的模板专业化。 sizeof...(args)为零时,将调用此方法。 请参阅此处的演示

对于乘法,您可以执行以下操作:

template<typename T, typename... ArgTypes>
int mult(T t, ArgTypes... args)
{
    return t * mult(args...);
}
template<> int mult() {
    return 1;
} 

不过,我不确定您打算减法做什么。 我们可以有数字的总和(加法)和数字的乘积(乘法),但是没有像???那样的东西。 (减)数字。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM