C++模板编程——可变参类模板

目录

1. 可变参类模板简介

2. 通过递归继承的方式展开可变参模板的参数包

2.1 类型模板参数的参数包展开

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

2.3 模板模板参数的参数包展开

3. 通过递归组合的方式展开可变参模板的参数包

4. 通过元组和递归调用展开参数包

4.1 元组的概念和演示

4.2 通过元组和递归调用来展开参数包

5. 可变参基类

6. 特化

7. 后记


1. 可变参类模板简介

可变参类模板和可变参函数模板类似,也支持0个多个模板参数。可变参模板参数包的展开也有多种方法,本文将介绍几种常用的可变参类模板的参数包展开方法。

2. 通过递归继承的方式展开可变参模板的参数包

2.1 类型模板参数的参数包展开

#include <iostream>

using namespace std;

template<typename... Args>
class VarArgClass
{
public:
	VarArgClass(Args... args)
	{
		cout << "   0x" << hex << this << " VarArgClass 泛化版本构造函数" << endl;
	}
};

template<typename First, typename... Others>
class VarArgClass<First, Others...> :public VarArgClass<Others...>
{
public:
	VarArgClass(First first, Others... others)
		:m_member(first), VarArgClass<Others...>(others...)
	{
		cout << m_member << ", 0x" << hex << this << " VarArgClass 偏特化构造函数" << endl;
	}
private:
	First m_member;
};

int main()
{
	VarArgClass<int, double, char> obj(1, 2.0, 'a');
}

运行结果如下图所示:

在main函数中,VarArgClass<int,double,char> obj(1,2.0,'a');这句代码,促使编译器实例化了VarArgClass<int, double, char>模板类,它又继承自VarArgClass<double, char>类(它也会被实例化),同时它又继承自VarArgClass<char>类,该类继承自VarArgClass<>类(泛化版本)。

它们之间的继承关系如下图所示:

另一方面,在派生类对象的实例化过程中会先调用基类的构造函数再执行自己的构造函数,再加这种链式继承关系,所以会从上往下依次调用各自的构造函数 。

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

参数包不仅可以是类型的,也可以是非类型的。

#include <iostream>
using namespace std;

template<int... NTArgs>
class VarArgNotTypeClass
{
public:
	VarArgNotTypeClass()
	{
		cout << "非类型可变参数类模板——泛化版本构造函数" << endl;
	}
};

template<int FirstArg, int... Others>
class VarArgNotTypeClass<FirstArg, Others...> :private VarArgNotTypeClass<Others...>
{
public:
	VarArgNotTypeClass()
	{
		cout << "非类型可变参数类模板——偏特化构造函数" << endl;
	}
};

int main()
{
	VarArgNotTypeClass<1, 2, 3, 4> vntc;
}

运行结果如下图所示:

执行结果的分析和上面类似,在这里就不进行分析了,下面给出继承关系图。

2.3 模板模板参数的参数包展开

模板模板参数也可以作为参数包(如果需要的话后面再单独开一节讲讲模板模板参数),下面给出示例代码:

#include <iostream>
#include <vector>
#include <list>
#include <deque>

template<typename T, 
	template<typename>typename... Containers>
class VarTArgClass
{
public:
	VarTArgClass()
	{
		cout << "VarTArgClass 泛化版本构造函数" << endl;
	}
};

template<typename T,
	template<typename>typename FirstContainer,
	template<typename>typename... OtherContainers>
class VarTArgClass<T, FirstContainer, OtherContainers...> :
	private VarTArgClass<T, OtherContainers...>
{
public:
	VarTArgClass()
	{
		cout << "VarTArgClass 偏特化构造函数" << ", T的类型为" << typeid(T).name() << endl;
	}
private:
	FirstContainer<T> m_container;
};

int main()
{
	VarTArgClass<float, vector, list, deque> varTArgClass;
}

 运行结果如下图所示:

3. 通过递归组合的方式展开可变参模板的参数包

上面的代码通过递归继承的方式来展开可变参模板的参数包,这一节通过递归组合的方式来展开可变参模板的参数包。

下面通过代码来体会一下:

#include <iostream>

using namespace std;

template<typename... Args>
class VarArgClass2
{
public:
	VarArgClass2()
	{
		cout << "VarArgClass2 泛化版本构造函数" << " 0x" << hex << this << endl;
	}
};

template<typename First, typename... Others>
class VarArgClass2<First, Others...>
{
public:
	VarArgClass2() :m_first(), m_others()
	{
		cout << "VarArgClass2 偏特化构造函数" << endl;
	}
	VarArgClass2(First first, Others... others) :
		m_first(first), m_others(others...)
	{
		cout << "VarArgClass2 偏特化构造函数, first=" << first
			<< ", 0x" << hex << this << endl;
	}
private:
	First m_first;
	VarArgClass2<Others...> m_others;
};

int main()
{
	VarArgClass2<int, double, char> obj1(1, 2.0, 'a');
}

运行结果如下:

因为递归组合的方式,所以每一个成员对象的this指针的值都不一样。

UML类图如下所示:

递归组合的方式也比较好理解,通过代码、运行结果还有类的UML图辅助理解一下。

下面来讲下一种参数包展开方式。

4. 通过元组和递归调用展开参数包

4.1 元组的概念和演示

元组能够容纳不同的类型的元素,相当于一种简单的容器。

下面通过一段简单的代码感受一下元组的使用。

#include <iostream>
#include <tuple>

using namespace std;

int main()
{
	tuple<float, int, char> t(1.2f, 3, 'a');

	cout << get<0>(t) << endl;
	cout << get<1>(t) << endl;
	cout << get<2>(t) << endl;
}

运行结果如下图所示:

get通过非类型模板参数来获取元组的内容,需要注意的是非类型模板参数一定是编译期常量

4.2 通过元组和递归调用来展开参数包

下面通过代码来学习一下:

#include <iostream>
#include <tuple>

using namespace std;

template<int count, int maxCount, typename... T>
class VarArgClass4
{
public:
	static void print(const tuple<T...>& t)
	{
		cout << "value = " << get<count>(t) << endl;
		VarArgClass4<count + 1, maxCount, T...>::print(t);
	}
};

template<int maxCount, typename... T>
class VarArgClass4<maxCount, maxCount, T...>
{
public:
	static void print(const tuple<T...>& t)
	{
		cout << "特化版本" << endl;
	}
};

template<typename... T>
void printTuple(const tuple<T...>& t)
{
	VarArgClass4<0, sizeof...(T), T...>::print(t);
}

int main()
{
	tuple<int, double, char> t(10, 3.14, 'a');
	printTuple(t);
}

运行结果如下图所示:

特化版本用于处理递归调用的终止情况,特化版本中可以什么都不做。

5. 可变参基类

某个类的基类也可以是可变参。

#include <iostream>

using namespace std;

template<typename... T>
class VarArgClass5 :public T...
{
public:
	VarArgClass5() : T()...
	{
		cout << "VarArgClass5 构造函数, 0x" << hex << this << endl;
	}
};

class A
{
public:
	A()
	{
		cout << "A 构造函数, 0x" << hex << this << endl;
	}
};

class B
{
public:
	B()
	{
		cout << "B 构造函数, 0x" << hex << this << endl;
	}
};

int main()
{
	VarArgClass5<A, B> varArgClass5;
	return 0;
}

运行结果如下图所示:

VarArgClass5继承了所有可变参类型,实例化VarArgClass5对象的过程中,首先会根据继承顺序依次调用基类的构造函数,再执行它自己的构造函数,运行结果中的构造函数调用顺序很容易理解。A类的子对象的this指针和VarArgClass5对象的this指针相同,而B类子对象的this指针发生了变化,这是因为A类是继承的第一个基类。多重继承的情况下,后面继承的类的子对象的this指针会进行调整。

6. 特化

 可变参模板不存在全特化,只有偏特化。

其实前面已经有展示过偏特化的代码,这里再简单介绍一下。

下面写一个泛化版本和偏特化版本的可变参类模板。

#include <iostream>

using namespace std;


template<typename... Args>
class VarArgClass6
{
public:
	VarArgClass6()
	{
		cout << "VarArgClass6<Args...> 泛化版本构造函数" << endl;
	}
};

template<typename First, typename... Others>
class VarArgClass6<First, Others...>
{
public:
	VarArgClass6()
	{
		cout << "VarArgClass6<First, Others...> 偏特化版本构造函数" << endl;
	}
};

template<typename Arg>
class VarArgClass6<Arg>
{
public:
	VarArgClass6()
	{
		cout << "VarArgClass6<Arg> 单参数偏特化版本构造函数" << endl;
	}
};

template<typename Arg1, typename Arg2>
class VarArgClass6<Arg1, Arg2>
{
public:
	VarArgClass6()
	{
		cout << "VarArgClass6<Arg1, Arg2> 双参数偏特化版本构造函数" << endl;
	}
};

int main()
{
	VarArgClass6<int> obj1;
	VarArgClass6<int, double> obj2;
	VarArgClass6<int, double, char> obj3;
}

运行结果如下图所示:

7. 后记

以上就是可变参类模板的全部内容,有讲的不对的地方敬请批评指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值