【可变参模板】可变参函数模板

1.基本含义

可变参模板的英文名叫 V a r i a d i c   T e m p l a t e Variadic \ Template Variadic Template,是 C + + 11 C++11 C++11引入的标准。可变参模板允许参数的数量不固定,可以有任意多个。

先看一个可变参函数模板,如下:

//基本含义

//可变参函数模板
template<typename ...T>
void myvtfunct(T...args) {
	std::cout << "*****begin*****\n";
	std::cout << "参数个数为:" << sizeof...(args) << "\n";
	std::cout << "类型个数为:" << sizeof...(T) << "\n";
	std::cout << "*****endl*****\n";
}

运行下面的代码:

void Test1() {
	myvtfunct();
	myvtfunct(10, 20);
	myvtfunct(10, 12.5, 'a', 3LL);

	//指定前几个参数
	myvtfunct<float, float>(1.0, 2.0, 3, 4, "abc");

}

得到下面结果:
在这里插入图片描述
我们了解一些概念:

( 1 ) (1) (1) t e m p l a t e < t y p e n a m e . . . T > template<typename... T> template<typename...T>中我们需要在 t y p e n a m e typename typename后面加入 . . . ... ...,表示当前的参数是参数包(可变参)

( 2 ) (2) (2) 在函数模板的形参中,我们传入的是 T . . . a r g s T...args T...args类型,其中 T . . . T... T...表示可变参类型(一包类型),而这时 a r g s args args称为一包参数(一堆参数)

( 3 ) (3) (3) s i z e o f . . . sizeof... sizeof... C + + 11 C++11 C++11标准引入,通常 s i z e o f . . . ( a r g s ) sizeof...(args) sizeof...(args)是计算可变参模板的参数包的大小,而 s i z e o f . . . ( T ) sizeof...(T) sizeof...(T)则是可变参模板的类型包的大小(个数)。

2.可变参函数模板的展开

通常,我们使用到可变参模板的时候需要对其参数包进行展开,而展开的方式比较固定,一般都是通过递归的方式来展开。

2.1使用同名递归函数来终止递归

其中一种展开方式是对参数包进行逐步展开,最后使用一个同名普通函数来结束递归。
如下,注意普通函数的前向声明:

void myfunc1(void); //前向声明
//可变参的展开
template<typename T, typename ...U>
void myfunc1(T firstarg, U... otherargs) {
	std::cout << "收到的参数值为:" << firstarg << "\n";
	myfunc1(otherargs...);
}

// 终止递归的函数
void myfunc1() {
	std::cout << "参数包展开时执行了终止函数!\n";
}

void Test2() {
	myfunc1(1, 2.5, "test");
}

调用结果为:
加粗样式
可以发现,对于参数包的展开,是每次减少一个参数,而最后空参会调用普通函数来结束递归。

2.2使用 i f   c o n s t e x p r if \ constexpr if constexpr来终止递归

C + + 17 C++17 C++17中引入了一个编译期间 i f if if的语句,可以在编译期间就确定是否会调用,具体而言, i f   c o n s t e x p r if \ constexpr if constexpr语句内的参数必须是常量或是编译期间就能确定的值。

根据这个特性,我们在可变参模板中使用它,然后在编译期间就能判断模板参数是否为 0 0 0来结束递归。
具体如下:


//编译期间if语句(constexpr if)
template<typename T,typename... U>
void myfunc2(T firstarg,U...otherargs) {

	//编译失败
	
	//size_t size = sizeof...(otherargs);
	//if constexpr (size > 0) { 

	if constexpr(sizeof...(otherargs)> 0) { //编译时确定
		std::cout << "收到的参数值为:" << firstarg << "\n";
		myfunc2(otherargs...);
	}
	else {
		std::cout << "递归终止!\n";
	}
}

当然,我们不能使用普通的 i f if if,因为 i f if if在编译期间时无法确定的。
另外,对于编译期间 i f if if和条件编译还是有区别的:

( 1 ) (1) (1)条件变量如果条件为假,则不会编译

( 2 ) (2) (2)编译期间 i f if if即使条件为假,也会编译

如下,我们没有定义 _ M Y D E F \_MYDEF _MYDEF,自然条件编译就不成立,因此不会编译,而 i f   c o n s t e x p r if \ constexpr if constexpr即使为假,仍然会编译:

void Test4() {

#ifdef _MYDEF
	func();
#endif

	//以下代码编译失败
	const bool flag = false; //必须是常量
	if constexpr (flag) {
		func(); //即使为假也会编译
	}
}

因此,由于我们没有定义函数 f u n c func func,所以 i f   c o n s t e x p r if \ constexpr if constexpr括号内的内容将编译失败。

3.可变参函数模板的重载

和普通函数模板一样,可变参的函数模板也能重载,而调用先后顺序和函数模板一样,先是普通函数,然后才是函数模板:


//重载
template<typename ...T>
void myfunc(T...arg) {
	std::cout << " myfunc(T...arg)执行了!\n";
}

//指针版本
template<typename ...T>
void myfunc(T*...arg) {
	std::cout << " myfunc(T*...arg)执行了!\n";
}

//普通函数
void myfunc(int arg) {
	std::cout << " myfunc(int arg)执行了!\n";

}

void Test5() {
	myfunc(1); //优先执行普通函数

	int* t = new int(1);
	myfunc(t);//调用重载的指针版本

	myfunc(1LL); //最后才是函数模板
}

调用结果如下:
在这里插入图片描述

先是调用普通函数,然后再调用了有修饰符的可变参函数模板,最后才是一般的可变参函数模板。

  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值