文章目录
前言
之前一直没有想法去了解可变参数,因为知道大概原理,就懒得了解细节了,今天偶然下发现了一篇写的很具参考意义的文章,就想着搬运一下。
搬运地址
https://www.cnblogs.com/qicosmos/p/4325949.html
一、可变参数定义
typename…表示有0-N个参数,可以输入0个参数也可以输入没有指定次数的参数。他大大拓展了模板的使用。
template<typename ...T1>
void fun(T1... Args){}
贴原文
可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号“…”。比如我们常常这样声明一个可变模版参数:template<typename…>或者template<class…>,一个典型的可变模版参数的定义是这样的:
template <class… T>
void f(T… args);
上面的可变模版参数的定义当中,省略号的作用有两个:
1.声明一个参数包T… args,这个参数包中可以包含0到任意个模板参数;
2.在模板定义的右边,可以将参数包展开成一个一个独立的参数。
上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。
二、可变参数个数求取
sizeof…(Args)可以求取参数个数
#include <iostream>
template<typename ...T1>
void fun(T1... Args)
{
std::cout << sizeof...(Args) << std::endl;
}
int main()
{
int a = 1, b = 2;
fun();//0
fun(a);//1
fun(a,b);//1
return 0;
}
三、可变参数模板函数提取
1.递归处理,每次提取一个。
#include <iostream>
//既然是递归就要给出终止条件,因此当参数剩余1个时就要给出终止条件。
template<typename First>
void fun(First t)
{
std::cout << t << std::endl;
}
//递归去提取值,每次拿出一个。
template<typename First, typename ...Rest>
void fun(First t, Rest... rest)
{
std::cout << t << std::endl;
//这里常用的方式std::forward<Rest>(rest)...的意思是对每一个都进行同样的处理。实际表述是(std::forward<1>(1)...std::forward<n>(n)))
fun(rest...);
}
int main()
{
int a = 1, b = 2;
fun(a, b);
return 0;
}
2. 逗号表达式方式去展开(很强的想法)
#include <iostream>
template<typename T>
void fun(T t)
{
std::cout << t << std::endl;
}
template<typename... T>
void fun(T... args)
{
//利用,会先执行前边,后执行后边,最后取后边那个值
//(fun(args),0)...实际就是{(fun(1),0),,,(fun(n),0)}
//调用顺序根据,原则先fun(args)调用完事后,在调用0赋值给数组。
int arr[] = { (fun(args),0)...};
}
int main()
{
int a = 1, b = 2;
fun(a, b);
return 0;
}
三、可变参数模板类提取
1.递归处理,每次提取一个。(特化最后的结果)
//提取传入所有类型中最后的类型
#include <iostream>
template<typename T, typename... Rest>
struct train
{
using type = typename train<Rest...>::type;
};
template<typename T>
struct train<T>
{
using type = T;
};
int main(void)
{
train<int, bool, double>::type a;
train<int, bool>::type b;
return 0;
}
2. 继承方式提取可变参数
#include <iostream>
//对第一个类型重新定义。
template<bool B, typename F, typename... T>
struct MyStruct
{
using type = F;
};
//利用特化对第二个参数类型进行重新定义
template<typename F, typename N, typename... T>
struct MyStruct<false, F, N, T...>
{
using type = typename MyStruct<true, N, T...>::type;
};
//当参数是一个或者零个时让内部值为false
template<typename... T>
struct is_type
{
static constexpr bool value = false;
};
//当参数是两个以上时取第二个参数为新类型,就是利用继承的方式对可变参数进行了解析
template<typename F, typename N, typename... T>
struct is_type<F, N, T...> : MyStruct<false, F, N, T...>
{
static constexpr bool value = true;
};
int main(void)
{
bool a = is_type<int>::value;
bool b = is_type<int, double>::value;
is_type<int, double>::type c;
return 0;
}
总结
可变参数对于想学模板变成是必须要掌握的。只有看懂可变参数提取,才能看懂源码里的各种操作。