转自:https://blog.csdn.net/m0_43383220/category_10485824.html
1 模板参数推导
1.1,C++17可对类模板的参数类型进行推导。
示例
1. 使用工厂方法 make_Type 构造对象(c++17 之前)
auto myPair = std::make_pair(42,"hello world"); //make_pair 是一个模板函数, //编译器可根据输入的模板参数类型推导出模板函数的参数类型, //不用写成std::make_pair<int,std::string>; 2. c++17对类模板进行类型推导 std::pair my_pair{12,"hello world"}; //推导为std::pair<int,std::string> auto ptr = new std::pair{13,"hello world"}; // 推导为:std::pair<int,std::string> //lock guard std::mutex mtx; std::lock_guard lck(mtx); //推导为:std::lock_guard<std::mutex> //array std::array arr{1,2,3}; //推导为:std::array<int,3> //tuple std::tuple t1{1,2,3}; //ok std::tuple<int,int,int> t2{1,2,3}; // ok std::tuple<int> t3{1,2,3}; //推导错误,模板参数类型要么写全,要么不写。
推导说明
举例 std::array 的推导规则。
template<typename _Tp, typename... _Up>
array(_Tp, _Up...)
-> array<enable_if_t<(is_same_v<_Tp, _Up> && ...), _Tp>,
1 + sizeof...(_Up)>;
//std::enable_if_t: std::enable_if<expression>::type
//std::is_same_v: std::is_same<T,U>::value
2. 折叠表达式
C++ 17 对C++ 11中的可变参数模板进行了改进,使代码更加简洁易懂。
语法
- ( 形参包 op … )
- ( … op 形参包 )
- ( 形参包 op … op 初值 )
- ( 初值 op … op 形参包 )
形参包
含未展开的形参包且其顶层不含有优先级低于转型的运算符的表达式
初值
不含未展开的形参包且其顶层不含有优先级低于转型的运算符的表达式
注意3,4只有这两种形式。即...必须位于中间,下面是不对的:
( op … op 初值 形参包 )
示例
递归函数求和
c++17 之前可变参数模板
//对加法的空包进行定义 即对递归的出口定义 auto Sum(){//c++14 auto返回值 return 0; } template<typename T1,typename... T> auto Sum(T1 s,T... ts){ return s + sum(ts); }
c++17 之后可变参数模板
auto Sum(){ //c++14 auto返回值 return 0; } //1.( 形参包 op ... ) template<typename ...Args>auto Sum1(Args ...args){ return (args + ...);//第一个参数展开+后面的在括号内递归展开。
//1+... => 1+(2+...)
//看红色点点点被替换了。再变一步:1+(2+...) => 1+(2+())点点点用括号替换,
//以前的参数剩下是3,4,5,所以以前的...递归展开是 3+... 即 1+(2+(3+...)) } //2. ( ... op 形参包 ) template<typename ...Args> auto Sum2(Args ...args){ return (... + args);//后面的在括号内递归展开+第一个参数展开
//递归就是这样变化
// () 即(... + 5) 变成
// (()) 即((...+4)+5) 变成
// ((())) 即(((...+3)+4)+5)
// (((()))) 即((((...+2)+3)+4)+5)
// ((((())))) 即(((((1)+2)+3)+4)+5)
} //3. ( 形参包 op ... op 初值 ) template<typename ...Args> auto Sum3(Args ...args){ return (args + ... + 0);//第一个args先拿出来,再后面的...要和0结合。
//1+(...+0)=>1+(2+(...+0)) => } //4. ( 初值 op ... op 形参包 ) template<typename ...Args> auto Sum4(Args ...args){ return (0 + ... + args);//(0+...)+5=》((0+...)+4)+5=> } auto res1 = Sum1(1,2,3,4,5); //展开为:Sum1(1 + (2 + (3 + ( 4 + 5)))) auto res2 = Sum2(1,2,3,4,5); //展开为:Sum2((((1 + 2) + 3) + 4) + 5); auto res3 = Sum3(1,2,3,4,5); //展开为:Sum3(1 + (2 + (3 + (4 + (5 + 0))))) auto res4 = Sum4(1,2,3,4,5); //展开为:Sum4(((((0 + 1) + 2) + 3) + 4) + 5)
将一元折叠用于零长包展开时,仅允许下列运算符。
逻辑与
(&&),空包默认值为true
;逻辑或
(||),空包默认值为false
;逗号运算符
(,),空包默认值为void()
。
输出函数模板入参
template<typename ...Args> //定义了可变参数的类型 void PrintArgs(Args&&... args){ //定义函数,使用类型 (std::cout << ... << std::forward<Args>(args)) << std::endl; } PrintArgs("hello","world",1,3,4); //输出参数无分隔:helloworld134 //2. template<typename ...Args> void PrintArgs2(Args&&... args){
//使用函数参数 Separator: auto Separator = [](const auto& v){//入参不一致,用auto进行推导 std::cout << v << ' '; //以空格分隔 }; (..., Separator(std::forward<Args>(args)));//给Separator函数一个个传进去值!!! } PrintArgs2("hello","world",1,3,4);//输出:hello world 1 3 4