河北邯郸手机网站建设,时事新闻2022最新10月,湖北省建设规划网站,wordpress 回复楼层可变参数模板
可变参数模板#xff08;variadic template#xff09;让您能够创建这样的模板函数和模板类#xff0c;即可接收可变数量的参数。这里介绍可变参数模板函数。例如#xff0c;假设要编写一个函数#xff0c;它可接受任意数量的参数#xff0c;参数的类型只需…可变参数模板
可变参数模板variadic template让您能够创建这样的模板函数和模板类即可接收可变数量的参数。这里介绍可变参数模板函数。例如假设要编写一个函数它可接受任意数量的参数参数的类型只需是 cout 能够显示的即可并将参数显示为用逗号分隔的列表。请看下面的代码
int n 14;
double x 2.71828;
std::string mr Mr. String objects!;
show_list(n, x);
show_list(x*x, !, 7, mr);这里的目标是定义 show_list()让上述代码能够通过编译并生成如下输出
14, 2.71828
7.3805, !, 7, Mr. String objects!要创建可变参数模板需要理解几个要点
模板参数包parameter pack)函数参数包展开unpack参数包递归。
模板和函数参数包
为理解参数包的工作原理首先来看一个简单的模板函数它显示一个只有一项的列表
templatetypename T
void show_list0(T value) {std::cout value , ;
}在上述定义中有两个参数列表。模板参数列表只包含T而函数参数列表只包含 value。下面的函数调用将模板参数列表中的 T 设置为 double将函数参数列表中的 value 设置为 2.15
show_list0(2.15);C提供了一个用省略号表示的元运算符meta-operator让您能够声明表示模板参数包的标识符模板参数包基本上是一个类型列表。同样它还让您能够声明表示函数参数包的标识符而函数参数包基本上是一个值列表。其语法如下
templatetypename...Args // Args is a template parameter pack
void show_list1(Args...args) // args is a function parameter pack
{
...
}其中Args 是一个模板参数包而 args 是一个函数参数包。与其他参数名一样可将这些参数包的名称指定为任何符合 C 标识符规则的名称。Args 和 T 的差别在于T 与一种类型匹配而 Args 与任意数量包括零的类型匹配。请看下面的函数调用
show_list1(S, 80, sweet, 4.5);在这种情况下参数包 Args 包含与函数调用中的参数匹配的类型char、int、const char * 和 double。
下面的代码指出 value 的类型为 T
void show_list0(T value)同样下面的代码指出 args 的类型为 Args
void show_list1(Args... args) // args is a function parameter pack更准确地说这意味着函数参数包 args 包含的值列表与模板参数包 Args 包含的类型列表匹配——无论是类型还是数量。在上面的示例中args 包含值’S’、80、“sweet” 和 4.5。
这样可变参数模板 show_list1() 与下面的函数调用都匹配
show_list1();
show_list1(99);
show_list1(88.5, cat);
show_list1(2,4,6,8, who do we, std::string(appreciate));就最后一个函数调用而言模板参数包 Args 包含类型 int、int、int、int、const char * 和 std::string而函数参数包 args 包含值 2、4、6、8、“who do we” 和 std::string(“appreciate”)。
展开参数包
但函数如何访问这些包的内容呢索引功能在这里不适用即您不能使用 Args[2] 来访问包中的第三个类型。相反可将省略号放在函数参数包名的左边将参数包展开。例如请看下述有缺陷的代码
templatetypename...Args // Args is a template parameter pack
void show_list1(Args... args) // args is a function parameter pack
{show_list1(args...); // passes unpacked args to show_list1()
}这是什么意思呢为何说它存在缺陷
假设有如下函数调用
show_list1(5, L, 0.5);这将把5、‘L’ 和 0.5 封装到 args 中。在该函数内部下面的调用
show_list1(5, L, 0.5);将展开成如下所示
show_list1(5, L, 0.5);也就是说args 被替换为三个存储在 args 中的值。因此表示法 args… 展开为一个函数参数列表。不幸的是该函数调用与原始函数调用相同因此它将使用相同的参数不断调用自己导致无限递归这存在缺陷。
在可变参数模板函数中使用递归
虽然前面的递归让 show_list1() 成为有用函数的希望破灭但正确使用递归为访问参数包的内容提供了解决方案。这里的核心理念是将函数参数包展开对列表中的第一项进行处理再将余下的内容传递给递归调用以此类推直到列表为空。与常规递归一样确保递归将终止很重要。这里的技巧是将模板头改为如下所示
templatetypename T, typename... Args
void show_list3( T value, Args... args)对于上述定义show_list3() 的第一个实参决定了 T 和 value 的值而其他实参决定了 Args 和 args 的值。这让函数能够对 value 进行处理如显示它。然后可递归调用 show_list3()并以 args… 的方式将其他实参传递给它。每次递归调用都将显示一个值并传递缩短了的列表直到列表为空为止。下面的程序提供了一种实现它虽然不完美但演示了这种技巧。
// variadic1.cpp -- using recursion to unpack a parameter pack
#includeiostream
#includestring// definition for 0 parameters -- terminating call
void show_list3() {}// definition for 1 or more parameters
templatetypename T, typename... Args
void show_list3( T value, Args... args) {std::cout value , ;show_list3(args...);
}int main() {int n 14;double x 2.71828;std::string mr Mr. String objects!;show_list3(n, x);show_list3(x*x, !, 7, mr);return 0;
}程序说明 请看下面的函数调用 show_list3(x*x, !, 7, mr);第一个实参导致 T 为 doublevalue 为 x*x。其他三种类型char、int 和 std::string将放入 Args 包中而其他三个值‘!’, 7 和 mr将放入 args 包中。 接下来函数 show_list3() 使用 cout 显示 value大约为 7.38905和字符串“”。这完成了显示列表中第一项的工作。 接下来是下面的调用 show_list3(args...);考虑到 args… 的展开作用这与如下代码等价 show_list3(!, 7, mr);前面说过列表将每次减少一项。这次 T 和 value 分别为 char 和 ‘!’而余下的两种类型和两个值分别被包装到 Args 和 args 中下次递归调用将处理这些缩小了的包。最后当 args 为空时将调用不接受任何参数的 show_list3()导致处理结束。 上面的程序的输出如下 14, 2.71828, 7.38905, !, 7, Mr. String objects!, 改进 可对 show_list3() 做两方面的改进。当前该函数在列表的每项后面显示一个逗号但如果能省区最后一项后面的逗号就好了。为此可添加一个处理一项的模板并让其行为与通用模板稍有不同 // definition for 1 parameter
templatetypename T
void show_list3(T value) {std::cout value \n;
}这样当 args 包缩短到只有一项时将调用这个版本而它打印换行符而不是逗号。另外由于没有递归调用 show_list3()它也将终止递归。 另一个可改进的地方是当前的版本按值传递一切。对于这里使用的简单类型来说这没问题但对于 cout 可打印的大型类来说这样做的效率很低。在可变参数模板中可指定展开模式pattern。为此可将下述代码 show_list3(Args... args);替换为如下代码 show_list3(const Args... args);这将对每个函数参数应用模式 const。这样最后分析的参数将不是 std::string mr而是 const std::string mr。 下面的程序包含这两项修改。 // variadic2.cpp
#includeiostream
#includestring// definition for 0 parameters;
void show_list() {}// definition for 1 parameter
templatetypename T
void show_list(const T value) {std::cout value \n;
}// definition for 2 or more parameters
templatetypename T, typename... Args
void show_list(const T value, const Args... args) {std::cout value , ;show_list(args...);
}int main() {int n 14;double x 2.71828;std::string mr Mr. String objects!;show_list(n, x);show_list(x*x, !, 7, mr);return 0;
}该程序的输出如下 14, 2.71828
7.38905, !, 7, Mr. String objects!