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:谢谢大家进行减法运算,是的,减法运算不是可交换的,因此它实际上不适用于这种递归模板。
答案 0 :(得分:3)
这是我的解释尝试。
首先:
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)
谁能详细解释此递归模板如何工作?
我可以尝试。
首先有
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
之间的和与后面add(args...)
之间的和(args...
)。
因此,add(args...)
不为空时,int add(T t, ArgTypes... args)
递归地称为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); }
答案 2 :(得分:1)
这是相当普遍的递归variadic template。这个想法是我们使用递归
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)的一般形式不可用,因为a-b != b-a
产生了歧义。还可以进行除法和其他非交换运算。您将必须澄清操作顺序。
答案 3 :(得分:0)
递归有一个base case。因此,您可以将template<> int add()
视为涵盖T
是整数的基本情况的模板专业化。 sizeof...(args)
为零时将调用此方法。参见Demo Here。
对于乘法,您可以这样做:
template<typename T, typename... ArgTypes>
int mult(T t, ArgTypes... args)
{
return t * mult(args...);
}
template<> int mult() {
return 1;
}
我不确定您打算减法做什么。我们可以有数字的总和(加法)和数字的乘积(乘法),但是没有像???那样的东西。 (减)数字。