c++中如何使用不定参数的函数(学习笔记)

在c++中我们需要同种函数拥有不同种的功能时,首先想到的函数重载,但是重载种类比较多时我们会使用模板函数(或者是模板函数重载以及模板函数的具体化)来帮助我们更加泛化,但这些方法的特点是函数参数的个数是确定的,假如我们需要参数个数是可以动态变化的话,上面的几个方法就不够用了。这时我们就可以使用variadic template以及initializer_list来解决这个问题。
用print函数来演示上述两种方法

void print() {}
template <typename T,typename ... Types>
void print(T first ,Types ... rest)
{
	cout << first << endl;
	print(rest...);
}

简单分析一下,上面使用了这个…这个非常关键的东西pack,这个包的概念是比较重要的。可以这样理解,…在模板参数列表中放在typename的后面,代表有…个typename,然后把这…称为template parameter packs(模板参数包),并起名为Types。
函数中的…被称为function parameter types pack(函数参数类型包),这个pack可以给个名字rest。
进入函数打印完first之后进入递归,这时我们传的参数是刚才的pack,这个pack再分解为一个类型加一个包,依此类推,直到后面分解为一个类型加0个包,这也就是我们为什么要定义一个无参数的空的print函数。
假如需要了解到我们传进去的参数个数有多少个,我们可以用sizeof运算符

sizeof...(rest) + 1;

注意这个…的位置,放错位置会报莫名其妙的错误。测试程序可以是

int main()
{
	print(1.0,2,"aaa",'h');
}

然后他就能打印出所有的参数了。
第二种是initializer_list,这时一个类模板,所以我们使用的时候需要为它指定类型,当然,这个类型也可以是模板。实例如下。

//initialize_list 迭代版本的print
#include<initializer_list>
#include<string>
template<typename T>
void print(initializer_list<T> iniList)
{
	for (const auto& i : iniList)
	{
		cout << i << endl;
	}
}

这个用法本身比较简单,不过从代码我们也可以看出它可以任意个数,但不能任意类型,必须是同种类型。而且类型必须严格一致或者是可以进行类型转换的(所以你得清楚什么样的类型转换是允许或者说是安全的),函数的书写方式也有细节需要注意,放代码吧

//注意调用方式,须将参数用大括号括起来
int main()
{
	print<const char*>({ "bbb", "aaaa", "111" });	//运行通过
	print<string>({ "bbb", "aaaa", "111" });	//运行也通过,因为可以调用string 的构造函数把字符串转化为string类型
	print<int>({1.0})	//error,类型不匹配
	print<int>({static_cast<int>(1.0)}); 	//通过
	print<int>({1});	//通过
	print<double>({1});	//通过
	
	//另外一种初始化方式
	initializer_list<string> mystring = {"bbb", "aaaa", "111" };	//时刻注意这是一个类,所以也是可以创建对象的,这样就可以用我们习惯的方式调用print函数了。
	print(mystring);	//通过
	cin.get();
}

代码虽然看起来多,但是关键就是知道怎么样得类型才是允许的,安全的。
然后我们可以用这个方式做一些更好的事情,比如比较大小。
常用的比较大小是两个两个比较的,但是用这个就可以一次性比较多个。stl库里面的min函数的参数就是这个类。我们可以试着简单实现一下。

template<typename T>
T minFun(initializer_list<T> iniList)
{
	auto i = iniList.begin();
	auto found = i;
	while (++i != iniList.end())
	{
		if (*found > *i)
			found = i;
	}
	return *found;
}


int main()
{
	initializer_list<int> ini{8,2,3};
	int a = minFun(ini);
	cout << a << endl;
	cin.get();
}

运行过后是符合我们的基本功能的。

你可能感兴趣的:(c++学习)