11,模板泛化、模板特化、所占字节数、继承实现模板展开、using循环命名展开可变参数

模板泛化

当前不知道是什么类型,调用时才知道是什么类型

advanced.h

#pragma once
#include <iostream>
using namespace std;

//泛化:所有的模板参数类型都未定义,当使用时才知道
template<class T1,class T2/* typename ...ParamTypes*/>//类型和可变参数的类型都不知道
class FHello_Class
{
private:
	T1 a;
	T2 b;
};

学习.cpp

#include <iostream>
#include"advanced.h"

int main()
{
	FHello_Class<int, float>A;
	
	return 0;
}

模板特化

特化是一种模板技术

模板全特化

必须写一个模板泛化做匹配,否则用不了

模板特化是一种将模板参数替换为具体类型或值的过程

#pragma once
#include <iostream>
using namespace std;

//泛化:所有的模板参数类型都未定义,当使用时才知道
template<class T>
class FHello_Class
{
private:
	T1 a;
	T2 b;
};

//特化(全特化):模板特化是一种将模板参数替换为具体类型或值的过程
template<>
class FHello_Class<int>//给定的类型一定是具体的
{

};
/*template<>
class FHello_Class1<int, float>//因为没有对应的模板泛化,所以会报错
{

};*/


//函数全特化
template<class T>
void FunTest()
{

};
template<>
void FunTest<int>()
{

}

模板特化分为模板全特化和模板偏特化

全特化(Full Specialization)和偏特化(Partial Specialization)是C++中模板特化的两种形式。

全特化是指对完整的模板进行特化,也就是将模板中的所有参数都替换为具体的类型或值。全特化通过使用特定的类型或值提供完整的特化定义。例如:

template<typename T, int N>
class Array
{
public:
    T elements[N];
};

template<>
class Array<int, 5>
{
public:
    int elements[5];
};

在上面的例子中,我们定义了一个通用的模板类 Array,包含一个类型参数 T 和一个整数参数 N。然后我们使用全特化对类型参数为 int,整数参数为 5 的情况进行了特化。这意味着当我们使用 Array<int, 5> 这个类型时,将使用全特化的定义,即 Array<int, 5> 类型将有一个 int 类型的数组成员。

偏特化是指对模板中的部分参数进行特化。偏特化允许我们针对特定的参数组合提供不同的定义。例如:

template<typename T, typename U>
class Pair
{
public:
    T first;
    U second;
};

template<typename T>
class Pair<T, T>
{
public:
    T element;
};

在上面的例子中,我们定义了一个通用的模板类 Pair,包含两个类型参数 TU。然后我们使用偏特化对两个类型参数相同的情况进行了特化。这意味着当我们创建 Pair<int, int> 这样的实例时,将使用偏特化的定义,即 Pair<int, int> 类型将只有一个 int 类型的成员 element

总之,全特化是对完整的模板进行特化,将所有参数都替换为具体的类型或值;而偏特化是对模板中的部分参数进行特化,允许我们为特定的参数组合提供不同的定义。这两种特化形式使得C++中的模板更加灵活和强大。

通过模板偏特化获取类型所占字节数

偏特化加可变参数

#pragma once
#include <iostream>
using namespace std;

//泛化
template<class T,class ... ParamTypes>
class Flen
{
public:
	enum
	{
		Number = Flen<T>::Number + Flen<ParamTypes...>::Number//得到最终类型大小
	};
};

//偏特化
template<class Last>
class Flen<Last>
{
public:
	enum
	{
		Number = sizeof(Last)
	};
};
#include <iostream>
#include"advanced.h"

int main()
{
	Flen<int, float, double, int>len;//输出结果位20 (4+4+8+4+4)
	cout << len.Number << endl;
	return 0;
}

通过模板偏特化和宏获取类型所占字节数

…ParamTypes和ParamTypes…的区别

ParamTypes…:ParamTypes…将被展开为一系列类型参数,并传递给Flen模板的实例化
… ParamTypes:…是展开模板参数包的语法,而不是参数包本身的一部分。class … ParamTypes是用于展开模板参数包ParamTypes的语法

SpawnIndex<3, int, float, double>

在上面的代码中,3 是一个非类型模板参数,int, float, double 是类型模板参数。在 SpawnIndex 的定义中,
…ParamTypes 将类型模板参数完整地展开,将 int, float, double 分别作为一个参数展开。
而 ParamTypes … 将 int, float, double 作为一个整体展开。

advanced.h

#pragma once
#include <iostream>
using namespace std;

//泛化
template<class T,class ... ParamTypes>
class Flen
{
public:
	enum
	{
		Number = Flen<T>::Number + Flen<ParamTypes...>::Number//得到最终类型大小
	};
};

//偏特化
template<class Last>
class Flen<Last>
{
public:
	enum
	{
		Number = sizeof(Last)
	};
};

#define A(Name, ...)Flen<__VA_ARGS__>Name;//通过__VA_ARGS__接收

学习.cpp

#include <iostream>
#include"advanced.h"

int main()
{
	Flen<int, float, double, int>len;//输出结果位20 (4+4+8+4+4)
	cout << len.Number << endl;

	A(len2, int, float, double, int, long long);//输出结果位28 (4+4+8+4+4+8)
	cout << len2.Number << endl;
	
	return 0;
}

通过继承实现模板展开

using

using单独使用相当于为类或结构体起别名

using Helloc = SpawnIndex<10>::Type;

给类Type起了别名Helloc

advanced.h

#pragma once
#include <iostream>
using namespace std;

//定义
template<int ...>
struct HelloIndex 
{

};

//展开的中间值
template<int N,int ...ParamTypes>//N代表最大的,...ParamTypes代表数字
struct SpawnIndex :SpawnIndex<N - 1, N - 1, ParamTypes ...>
{

};

//终止,如果没有此会无限循环
template<int ... ParamTypes>
struct SpawnIndex<0, ParamTypes ...>
{
	typedef HelloIndex<ParamTypes...>Type;
};

学习.cpp

#include <iostream>
#include"advanced.h"

int main()
{
	using Helloc = SpawnIndex<10>::Type;//展开一个10,同时为类Type起了一个别名Helloc
	//using单独使用相当于给类或结构体起别名
	
	//查看展开
	cout << typeid(Helloc).name() << endl;
	//输出结果struct HelloIndex<0,1,2,3,4,5,6,7,8,9>

	
	SpawnIndex<3>::Type;
	//SpawnIndex<3>::Type;的展开过程
	// 
	//匹配到struct SpawnIndex :SpawnIndex<N - 1, N - 1, ParamTypes ...>进行运算
	//struct SpawnIndex :SpawnIndex< 2, 2>
	//            
	//struct SpawnIndex< 2, 2> :SpawnIndex< 1, 1, 2>
	//                   N  ParamTypes      N  N  ParamTypes 
	//左边是template<int N,int ...ParamTypes>的形式,右边是SpawnIndex<N - 1, N - 1, ParamTypes ...>
	// 
	//struct SpawnIndex< 1, 1, 2> :SpawnIndex< 0, 0, 1, 2>
	//因为N值为0了,展开到这一步会终止,之后匹配struct SpawnIndex<0, ParamTypes ...>此模板,进行下面步骤
	// 
	//typedef HelloIndex< 0, 1, 2>Type;

	return 0;
}

(ue4源码的模板综合了很多,如可变参数、特化、非特化)

通过using循环命名的方式来展开可变参数

advanced.h

#pragma once
#include <iostream>
using namespace std;

//定义
template<int ...>
struct HelloIndex 
{

};

//通过using展开的中间值
template<int N, int ...ParamTypes>
struct SpawnIndex 
{
	using Type = typename SpawnIndex<N - 1,N - 1, ParamTypes...>::Type;
	//编译器先发现Type,之后发现后面代码,之后一层层递归,递归过程中参数会改变
};

//循环终止
template<int ... ParamTypes>
struct SpawnIndex<0, ParamTypes ...>//最后在参数为0时运行最后模板
{
	typedef HelloIndex<ParamTypes...>Type;
};

学习.cpp

#include <iostream>
#include"advanced.h"

int main()
{
	using Helloc = SpawnIndex<10>::Type;

	//查看展开
	cout << typeid(Helloc).name() << endl;
	//输出结果:struct HelloIndex<0,1,2,3,4,5,6,7,8,9>

	return 0;
}


typename 是一个关键字,用于在模板编程中指示一个依赖类型。它通常用于声明模板中的类型参数,用于告知编译器某个名字代表一个类型

问题:为什么又两个N-1
在模板参数SpawnIndex<N, int ...ParamTypes>中,N - 1在每次递归调用时作为新的模板参数传递给SpawnIndex,而N - 1, N - 1则是作为递归调用的模板参数。这两个参数的作用如下:

首先,在每次递归调用时,N - 1作为新的模板参数传递给SpawnIndex<N - 1, N - 1, ParamTypes...>::Type,它用于更新N的值,实现递减。这样,在每次递归时,N的值都会减小,直到最后递归到SpawnIndex<0, ParamTypes...>,从而触发递归终止的模板。

其次,在递归调用中,N - 1, N - 1作为ParamTypes的一部分被传递给递归调用的模板参数列表。这样,新的模板参数列表中会包含N - 1, N - 1, ParamTypes...,并被传递给下一次递归调用。这样,每次递归调用时,ParamTypes列表都会逐渐增长,记录了调用的历史信息。

综上所述,N - 1主要用于控制递归调用的次数,而N - 1, N - 1在递归调用中记录了每次递归的历史信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值