首页 > 其他分享 >【可变参模板】可变参类模板

【可变参模板】可变参类模板

时间:2024-09-13 10:55:28浏览次数:3  
标签:std cout public 参数 可变 展开 模板 参类

可变参类模板也和可变参函数模板一样,允许模板定义含有0到多个(任意个)模板参数。可变参类模板参数包的展开方式有多种,以下介绍几种常见的方法。

一、递归继承展开

1.1类型模板参数包的展开

首先先看下面的代码:


//类型模板展开
//泛化版本
template<typename...Args>
class myclasst {

public:

	myclasst() {
		std::cout << "myclasst::myclasst()泛化版本执行了,this = " << this
			<< " sizeof...(Args) = " << (sizeof...(Args)) << "\n";

	}

};

template<typename First, typename... Others>
class myclasst<First, Others...> :private myclasst<Others...> { //继承偏特化版本

public:
	First m_i;

	myclasst() :m_i(0) {
		std::cout << "myclasst::myclasst()偏特化版本执行了,this = " << this
			<< " sizeof...(Others) = " << (sizeof...(Others)) << "\n";

	}


};

void Test1() {
	myclasst<int, float, float>myc;
}

这里,我们定义了一个可变参类模板的泛化版本,通过 s i z e o f . . . sizeof... sizeof...计算出参数数量,我们还打印了 t h i s this this指针。

然后,我们又定义了一个偏特化类型,这个偏特化类型继承了另一个偏特化版本。可以发现,继承的参数列表参数减少了一个。

我们运行后,得到以下结果。
容易发现, t h i s this this指针是一致的,而且打印的参数是从少到多的。
在这里插入图片描述
因此,我们可以得到结论:

对于这种继承来展开的参数包,我们是递归到空参,调用泛化版本构造了这样的一个类,作为祖宗类,然后再通过这个祖宗不断通过继承链展开。

由于是继承关系,所以 t h i s this this指针显然是相同的。

相应的关系如下图所示:
在这里插入图片描述

当然,如果我们对泛化版本进行一个空参的偏特化,如下:

//使用特殊的特化版本
template<>
class myclasst<> {
public:
	myclasst() {
		std::cout << "myclasst<>::myclasst()特殊的特化版本执行了,this = " << this << "\n";

	}
};

可以发现,这次最开始实例化的模板是这个空参的特化版本,注意这里并不是全特化(可变参模板不存在全特化)
在这里插入图片描述
有时,如果我们不需要使用泛化版本(就像上面的样例),直接声明即可:

//直接声明
template<typename...Args>
class myclasst;

1.2非类型模板参数包的展开

参数包不仅可以是类型的,也可以是非类型的。
看下面的范例:

//非类型模板参数的展开

//泛化版本
template<auto... FArgs>
class myclasst2 {
public:
	myclasst2() {
		std::cout << "myclasst2::myclasst2()偏特化版本执行了,this = " << this
			<< " sizeof...(Others) = " << (sizeof...(FArgs)) << "\n";

	}
};

//偏特化版本
template<auto First, auto... Others>
class myclasst2<First, Others...> :public myclasst2<Others...> {
public:
	myclasst2() {
		std::cout << "myclasst2::myclasst2()偏特化版本执行了,this = " << this
			<< " sizeof...(Others) = " << (sizeof...(Others)) << ",First = " << First << "\n";

	}
};

void Test2() {
	myclasst2<1, 2LL, 'a', 4, 5>myc;

}

可以发现,这里的参数列表使用了 a u t o auto auto,这样更灵活一些。实际上,如果想要固定类型,如 ( i n t , c h a r , d o u b l e ) (int,char,double) (int,char,double)等类型,也可以直接使用。

对于非类型参数包的展开,大体上和类型参数包展开一致,这里直接调用,得到:
在这里插入图片描述

同样,对于参数的展开是递归的,从最后开始实例化

1.3 模板模板参数的展开

对于参数列表是模板模板参数,并且是一个可变参数列表的类型,我们同样可以展开,写法上类似,只是繁琐了些。

参考下方代码:

//模板模板参数的展开
template<typename T, template<typename>typename... Container> //泛化版本
class myclasst3 {
public:
	myclasst3() {
		std::cout << "myclasst3::myclasst3()泛化版本执行了,this = " << this << "\n";

	}
};

//偏特化版本
template<typename T, template<typename>typename FirstContainer,
	template<typename>typename... OtherContainers>
class myclasst3<T, FirstContainer, OtherContainers...>
	:public myclasst3<T, OtherContainers...> {

public:
	myclasst3() {
		std::cout << "myclasst3::myclasst3()偏特化版本执行了,this = " << this
			<< " sizeof...(Others) = " << (sizeof...(OtherContainers)) << "\n";

	}
};

同样是继承链展开,我们下面写一个辅助函数来查看调用的信息:

//查看类型的辅助类
template<typename T, template<typename>typename... Container>
class myclasst4 :public myclasst3<T, Container...> {

public:
	myclasst4() {
		std::cout << "myclasst4::myclasst4()执行了,this = " << this << ", T的类型是:"
			<< typeid(T).name() << ",Container的参数个数是" << (sizeof...(Container)) << "\n";

	}

};
void Test3() {
	myclasst4<int, std::vector, std::list, std::deque, std::stack>myc;

}

这个辅助类继承自泛化版本,因此调用这个辅助类模板的时候,就会展开模板模板类型参数包:

在这里插入图片描述
这里使用了 t y p e i d typeid typeid来打印类型名,可以发现实例化出的容器类都是 i n t int int类型。

二、通过递归组合方式来展开参数包

2.1 递归组合展开

前面的展开方式基于继承,而递归组合方式是基于构造来展开的。
先看看下面的代码:

//通过递归组合展开

//泛化版本
template<typename... args>
class myclassT {
public:
	myclassT() {
		std::cout << "myclassT::myclassT()泛化版本执行了,this = " << this << "\n";

	}
};

//偏特化版本
template<typename First, typename...Others>
class myclassT<First, Others...> {
public:
	First m_i;
	myclassT<Others...>m_o;

	myclassT() :m_i(0) {
		std::cout << "myclassT::myclassT()偏特化版本执行了,this = " << this << "<sizeof...(Others) = "
			<< sizeof...(Others) << "\n";
	}

	//递归构造组合
	myclassT(First parf, Others... paro) :m_i(parf), m_o(paro...) {
		std::cout << "------------begin------------\n";
		std::cout << "myclassT::myclassT(parf,...paro)执行了,this = " << this << "\n";
		std::cout << "m_i = " << m_i << "\n";
		std::cout << "------------end------------\n\n";

	}

};

我们省去了繁琐的继承链,通过在泛化版本内部设置成员变量以及构造函数的方式来展开参数包。
在这里插入图片描述

我们会进行递归的构造,直到参数列表为空,调用泛化版本结束递归。
在这里插入图片描述
同样的,也能用特殊的特化版本来结束递归:

//特殊的特化版本
template<>
class myclassT<> {
public:
	myclassT() {
		std::cout << "myclassT::myclassT()特殊的特化版本执行了,this = " << this << "\n";

	}
};

运行测试代码:

void Test4() {
	myclassT<int, float, double, char>myc(1, 2.0f, 3.0, '4');
}

得到以下结果:
在这里插入图片描述

同样是递归展开,所以参数的构造也是从低向上,从右到左。而这一次,由于不是继承关系,所以 t h i s this this指针也不同。
在这里插入图片描述

非类型参数包和模板模板类型参数包的展开类似,这里就不赘述了。

三、通过元组和递归调用展开参数包

3.1元组的基本使用

元组 t u p l e tuple tuple是 C + + 11 C++11 C++11引入的类型,在元组内可以定义不同的类型,下面是元组的基本使用方法:

//元组的基本使用方法
void Test5() {
	//定义不同类型的元素
	std::tuple<int, char, float>mytuple(0, '1', 2.0f);

	//取出元组第i个元素,使用std::get<>函数模板
	std::cout << std::get<0>(mytuple) << "\n";

	//后一个类型可以自动推导
	std::cout << std::get<1>(mytuple) << "\n";
	std::cout << std::get<2>(mytuple) << "\n";

	//使用std::get<T>返回元组类型为T的值,如果存在多个相同类型会报错
	std::cout << "int类型为:" << std::get<char>(mytuple) << "\n";

}

其中 s t d : : g e t < > std::get<> std::get<>是一个函数模板,可以去除元组第 k k k个类型的数,也可以指定类型取出相应类型的值。

注意的是,如果是取出指定类型的值,必须是唯一的,不然将无法编译通过。

简单测试,运行后得到:
在这里插入图片描述

3.2使用元组来递归展开

借助元组可以定义不同类型的这个特点,我们可以进行参数包的展开:



//使用元组来递归展开
template<int mycount, int mymaxcount, typename...T>
class myclassT2 {
public:
	static void mysfunc(const std::tuple<T...>& t) {
		std::cout << "获取第" << mycount + 1 << "个元素,val = " << std::get<mycount>(t) << "\n";
		myclassT2<mycount + 1, mymaxcount, T...>::mysfunc(t); //自递归,每次取出后一个数
	}
};

//偏特化版本,用于结束递归
template<int mymaxcount, typename...T>
class myclassT2<mymaxcount, mymaxcount, T...> {
public:
	static void mysfunc(const std::tuple<T...>& t) {
		std::cout << "调用特化版本结束递归!\n";
	}
};

//辅助调用模板
template<typename...T>
void myfunctuple(const std::tuple<T...>& t) {
	myclassT2<0, sizeof...(T), T...>::mysfunc(t); //从0开始展开

}

这里, m a x c o u n t maxcount maxcount是需要展开的参数包的总大小,通过 s i z e o f . . . sizeof... sizeof...求得,mycount$是目前展开了多少个参数,初始为0 0 0 0,需要递归 + 1 +1 +1。

因此,当 m a x c o u n t = m y c o u n t maxcount = mycount maxcount=mycount时,需要调用一个特化版本来结束递归。

我们写了一个辅助模板,用于展开:
在这里插入图片描述

成员函数使用静态的,这样在编译期间就能确定,不使用静态的也行,只是这样需要创建类对象。

调用测试函数:

void Test6() {
	std::tuple<float, int, int>mytuple(1.0f, 2, 3);

	myfunctuple(mytuple);
}

成功展开了参数包:

在这里插入图片描述

标签:std,cout,public,参数,可变,展开,模板,参类
From: https://blog.csdn.net/Antonio915/article/details/142181542

相关文章

  • PbootCMS模板安全设置与加固方法
    为了确保PbootCMS模板的安全性和稳定性,可以采取一系列的安全设置与加固措施。下面是一些具体的步骤和建议:1.权限设置目录权限:调整关键目录的权限,防止未经授权的写入操作。/apps:禁止写入权限(chmod0555)/config:允许读写权限(chmod0777)/core:禁止写入权限(chmod0555)......
  • PbootCms模板搭建网站,可能会遇到内页无法正常访问的情况
    当你遇到PbootCMS内页无法正常访问的问题时,关闭伪静态并转为兼容模式是一种简单有效的解决方案。以下是详细的步骤:步骤详解登录后台管理系统打开你的PbootCMS网站后台管理系统。使用管理员账号登录。进入设置页面在后台管理系统的左侧导航栏中找到“设置”选项,并点击......
  • PbootCMS模板发布文章显示的默认作者如何修改?
    PbootCMS模板中默认作者的修改可以通过以下步骤完成:1.修改默认作者名称登录后台:登录PbootCMS后台管理界面。点击右上角的作者名称:在后台右上角,点击当前显示的作者名称(如“格展网络”)。进入密码修改页面:在弹出的菜单中选择“密码修改”。修改作者名称:......
  • PbootCMS网站模板如何做好防护
    为了保护PbootCMS网站模板免受攻击和未经授权的访问,可以采取一系列措施来增强其安全性。以下是根据提供的信息整理的一些推荐做法:1.更改敏感文件夹名称更改data文件夹名称:将 data 文件夹重命名,例如改为 mubanbaba@data。修改 config/database.php 文件中相应的地......
  • PbootCMS模板禁止数字id方式访问栏目,如/1/
    在PbootCMS中,如果你希望禁止用户通过直接输入数字ID的方式访问栏目,比如通过URL /1/ 进行访问,可以采取以下几种策略来实现这一目标:1.修改URL规则1.1重写规则配置.htaccess文件:编辑网站根目录下的 .htaccess 文件,增加或修改URL重写规则,使得所有数字ID的访......
  • PbootCMS模板自动清理runtime缓存
    runtime目录的作用runtime 目录位于PbootCMS的安装目录下,主要用于存储系统运行时产生的临时文件和缓存文件。这些文件包括但不限于:缓存文件日志文件临时文件随着时间的推移,runtime 目录中的文件会逐渐增多,占用大量磁盘空间。当文件过多时,会造成系统性能下降,甚至出现一......
  • Pbootcms模板留言“提交成功”的提示语修改
    如果你想要修改PbootCMS模板中留言提交成功后的提示语,可以通过编辑相关控制器文件来实现。具体步骤如下:定位文件:找到 apps/home/controller/MessageController.php 文件。修改提示语:在该文件中找到处理留言提交成功的逻辑,并修改相应的提示语。具体步骤1.定位......
  • [模板题] - 208. 实现 Trie (前缀树)
    题目链接208.实现Trie(前缀树)思路模板题-Trie树题解链接官方题解关键点无时间复杂度\(O(\sum_{i}\#\text{word}_{i})\)空间复杂度\(O(\sum_{i}\#\text{word}_{i})\)代码实现:classTrie:def__init__(self):self.children=[N......
  • 魔怔模板
    线段树structsegmenttree{ structnode{ intl,r; longlongsum,tag;} T[maxn*4]; longlongrepair(intp,longlongk){ returnT[p].tag+=k,T[p].sum+=k*(T[p].r-T[p].l+1);} voiddowndata(intp){ repair(2*p,T[p].tag),repair(2*p+......
  • 【办公】会议纪要模板
    会议纪要:软件代码资料释放讨论会会议时间:[具体日期],[开始时间]-[结束时间]会议地点:[线上/线下具体地点,如Zoom会议号、公司会议室等]主持人:[主持人姓名及职位]参会人员:[参会人员姓名1],[职位][参会人员姓名2],[职位][参会人员姓名3],[职位]...(根据实际情况列出所有参会人员)......