递归模板解释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:添加了调用堆栈作为社区参考
递归有一个base case. So you can think of template<> int add()
as the template specialization covering the base case when T
is an integer. This will be called when sizeof...(args)
is zero. See Demo Here。
对于乘法你可以这样做:
template<typename T, typename... ArgTypes>
int mult(T t, ArgTypes... args)
{
return t * mult(args...);
}
template<> int mult() {
return 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
,会产生歧义。还有除法和其他非交换运算。您将必须明确操作顺序。
Could anyone explain in detail how this recursive template does work?
我可以试试
首先你有
template<typename... ArgTypes>
int add(ArgTypes... args);
这是一个可变参数模板函数声明:您声明存在一个 add()
接收可变参数(零个或多个)参数的函数。
注意:您声明但未定义函数。
第二:你声明和 define
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...);
计算 return t
之间的总和以及以下 args...
.
之间的总和 (add(args...)
)
因此当 args...
(因为它是更好的匹配)不为空时 add(args...)
被递归调用 int add(T t, ArgTypes... args)
并且当 args...
为空时 int add(ArgTypes... 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
作为 returning 类型。
How to add more operations like multiplication and subtraction?
不知道减法(如何定义可变减法?)但是,对于乘法,您可以使用类似的东西(但基本情况必须 return 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); }
这是我的解释。
首先:
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
的函数,该函数采用 零 个参数。
所以,总结一下:
- 第二个声明定义了一个接受一个或多个 arguemnts
的函数
- 第三个声明定义了一个采用 零 个参数的函数
- 一起,这意味着我们有一个函数接受 零个或多个 个参数,
正如第一个声明所声明的那样。
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:添加了调用堆栈作为社区参考
递归有一个base case. So you can think of template<> int add()
as the template specialization covering the base case when T
is an integer. This will be called when sizeof...(args)
is zero. See Demo Here。
对于乘法你可以这样做:
template<typename T, typename... ArgTypes>
int mult(T t, ArgTypes... args)
{
return t * mult(args...);
}
template<> int mult() {
return 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
,会产生歧义。还有除法和其他非交换运算。您将必须明确操作顺序。
Could anyone explain in detail how this recursive template does work?
我可以试试
首先你有
template<typename... ArgTypes>
int add(ArgTypes... args);
这是一个可变参数模板函数声明:您声明存在一个 add()
接收可变参数(零个或多个)参数的函数。
注意:您声明但未定义函数。
第二:你声明和 define
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...);
计算 return t
之间的总和以及以下 args...
.
add(args...)
)
因此当 args...
(因为它是更好的匹配)不为空时 add(args...)
被递归调用 int add(T t, ArgTypes... args)
并且当 args...
为空时 int add(ArgTypes... 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
作为 returning 类型。
How to add more operations like multiplication and subtraction?
不知道减法(如何定义可变减法?)但是,对于乘法,您可以使用类似的东西(但基本情况必须 return 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); }
这是我的解释。
首先:
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
的函数,该函数采用 零 个参数。
所以,总结一下:
- 第二个声明定义了一个接受一个或多个 arguemnts 的函数
- 第三个声明定义了一个采用 零 个参数的函数
- 一起,这意味着我们有一个函数接受 零个或多个 个参数, 正如第一个声明所声明的那样。