C++变参模版

变参模版,即参数数目可变的模版,如printf()。声明变参函数时,需令函数参数列表包含一个省略号(...),变参模版也是如此:

template
class my_template
{};

对于某个模版,即便其泛化版本的参数固定不变,也能用变参模版进行偏特化

(C++语法规定,任何模版必须具备泛化形式的声明,不能以偏特化形式声明,尽管泛化的packaged_task没有实际作用,其用途是告诉编译器存在名为packaged_task的模版):

template	//无实际作用
class packaged_task;			//无实际作用
template
class packaged_task;

我们可以凭代码std::packaged_task声明一项任务,其以std::future的实例给出结果,以std::string和double类型作为参数,其中变参列表可以为空。

特性:

1.普通模版参数(上例的ReturnType)和可变参数(上例的Args)能在同一声明内共存。

2.特化版本中使用了组合标记Args...,模版具现化时各型别依次列出。

3.模版实例化中,出现的型别被全部捕获并打包成Args,可变参数Args称为参数包(parameter pack),用“Args...”还原参数列表称为包的展开(pack expansion)。

展开参数包

在任何需要模版型别列表的场合,我们均可以直接运用展开式,在另一模版的参数列表中展开(展开模式省略号在后面),展开模式可随意设定复杂的型别表达式,前提是参数包在型别表达式中出现,且该表达式以省略号结尾:

template
class dummy
{
	std::tuple data;
};

上面例子中,dummy类模版的参数在其tuple数据成员data的实例化中展开。
我们还可以依照某种模式创建元组,使其中的成员型别都是普通指针,甚至都是std::unique_ptr<>指针,目标型别对应参数包中的元素。

若模版参数列表用到了展开式,则模版无须再明文写出可变参数,参数包应准确匹配模版参数,所含参数数目必须相等:

template
struct dd
{
	std::pair data;
};
dd a;	//正确
dd b;			//错误,缺少第二型别
dd b;//错误,型别数过多

另一种用途是声明函数参数:

template
void func(Args ...args);

 一个函数的参数包还可以传递给另一个函数调用,后者在参数列表中设定好展开形式即可。以下例子通过std::forward<>保有函数参数的右值属性,“...”为展开位置

template
void bar(Args&& ...args)
{
	work(std::forward(args)...);
}
int i;
bar(i, 3.14, std::string("Circle"));

其展开形式为:

template<>
void bar(
	int& args_1,
	double&& args_2,
	std::string&& args_3)
{
	work(std::forward(args_1),
		 std::forward(args_2),
		 std::forward(args_3)
	);
}

第一项参数为以左值引用方式传入,余下参数则以右值引用传递。

我们可以通过sizeof...(p)运算符,确定参数包所含元素数目,其中p为参数包:

template
unsigned count_args(Args ...args)
{
	return sizeof...(Args);
}

sizeof...运算符求得的值是常量表达式,与普通sizeof运算符一样,故其结果可用于设定数组长度。 

你可能感兴趣的:(C++语言特性,c++)