前言:

c++ 11 引入和变参模板用来处理任意数量模板参数的场景。




变参模板函数 (C++11/14 迭代展开 | 一个模板参数和一个模板参数包)

#include <iostream>
#include <string>

void MyPrint(){
  std::cout << "  end" << std::endl;
}

template<typename FirstArgInArgs, typename... Args>
void MyPrint(FirstArgInArgs first,Args... following)
{
  std::cout << first << "|" ;
  MyPrint(following...);
}

int main()
{
  MyPrint<std::string,int,int,long,char,double>("let's start",1,2,10000,'K',3.14);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

运行结果

let's start|1|2|10000|K|3.14| end

需要注意的几点:

  1. 变参模板中,不能只有模板参数包(列表) Args,必须要包含一个模板参数(FirstArgInArgs)在模板参数包(Args)之前,否则会编译不通过。
  2. C++ 11/14只能使用递归的方式展开模板参数包(列表),由于使用了递归,因此需要一个递归终结条件 —— “终止函数”,当参数包长度为0的时候,这个终止函数被调用,形式为模板函数的无参数版本,比如 例子中的 void MyPrint()。这个函数必须要有且要在模板参数前面定义或提前声明。
  3. 使用 C++ 11/14 的递归方式展开参数包时,第一个模板参数 FirstArgInArgs 用来在递归场景下接收参数包里的第一个参数,以例子来说明,递归逻辑如下
  • 第一次调用  MyPrint<std::string,...>("let's start",following...) , 这里 FirstArgInArgs 用来接收 let's start 这个 string 类型。这个时候通过 std::cout << first << "|" 输出了 let's start 。这里需要注意,FirstArgInArgs 并不会被当作一个特殊的参数用来接收 let's start,而是因为其在Args之前,故而被用来为 FirstArgInArgs 服务。
  • 第二次调用 MyPrint<int,...>(1,following...),这里 FirstArgInArgs 用来接收 int , 所以 1 被输出了。
  • 第三次,第四次同理...



变参模板函数 ( C++11/14 迭代展开 | 一个固定模板参数,一个模板参数用于解包,一个模板参数包 )

#include <iostream>
#include <string>

template<typename element>
void MyPrint(element e)
{
  std::cout << "1st-" << e << std::endl;
}

template<typename pipeline, typename element, typename... elementlist>
void MyPrint(pipeline p,element e,elementlist... list)
{
  std::cout << "2nd-" << p << std::endl;
  MyPrint(e,list...);
}

int main()
{
  MyPrint<std::string,std::string,std::string,std::string,std::string,std::string>("pipeline","appsrc","filter1","filter2","filter3","appsink");
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

运行结果

2nd-pipeline
2nd-appsrc
2nd-filter1
2nd-filter2
2nd-filter3
1st-appsink

单参数模板是通用模板void MyPrint(element e),也是迭代退出的“终止函数”, 可以看到和上章节z中以无参数版本 void MyPrint() 作为 “终止函数” 不同,这里的终止函数是 有一个参数的。那么为什么有这个差别呢?

蹊跷就在于模板函数里迭代调用的那个函数,这里是 MyPrint(e,list...) ,当 list... 为空的时候,MyPrint(e,list...) 就变成了 MyPrint(e), 这恰恰就是只有一个参数的MyPrint版本。


而上一章中的 模板函数是 MyPrint(following...) , 当 following... 是空的时候, MyPrint(following...) 就蜕变成 MyPrint()了。


所以在写终止函数的时候要根据模板函数的迭代函数形式来写,不然会出现没有终止函数的情况。




变参模板 ( C++11/14 迭代展开 | 一个固定模板参数,一个模板参数用于解包,一个模板参数包 (gstreamer里面可以用来编写 add 和 link 函数))

上面的两个例子比较了终止函数并不是只能有一种无参版本。但是第二个例子在逻辑控制方面没有描述清楚,下面将结合代码梳理递归展开的真实流程。

#include <iostream>
#include <string>
 
using namespace std;

//A
template<typename GSTBIN, typename ITOR>
void AddOne(GSTBIN bin, ITOR itor)
{
  cout << "add " << itor << " to " << bin << endl;
}

//C
template<typename GSTBIN>
void AddToBin(GSTBIN bin)
{
  cout << "end" << endl;
}

//D
template<typename GSTBIN, typename ITOR, typename... LIST>
void AddToBin(GSTBIN bin, ITOR itor, LIST... list)
{
  AddOne(bin,itor);
  AddToBin(bin,list...);
}

int main()
{
  AddToBin<std::string,std::string,std::string,std::string,std::string,std::string>("bin","appsrc","filter1","filter2","filter3","appsink");
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.

结果

add appsrc to bin
 add filter1 to bin
 add filter2 to bin
 add filter3 to bin
 add appsink to bin
 end
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

流程

//1st use D
 //             bin   itor          LIST...
 AddOne("bin","appsrc",{"filter1","filter2","filter3","appsink"})
 {
   AddOne("bin","appsrc");  //use A  输出 add appsrc to bin
   AddToBin("bin",{"filter1","filter2","filter3","appsink"});
 }
//2nd use D
 //             bin    itor        LIST...
 AddOne("bin","filter1",{"filter2","filter3","appsink"})
 {
   AddOne("bin","filter1");  //use A  输出 add filter1 to bin
   AddToBin("bin",{"filter2","filter3","appsink"});
 }
//3rd use D
 //             bin     itor      LIST...
 AddOne("bin","filter2",{"filter3","appsink"})
 {
   AddOne("bin","filter2");  //use A  输出 add filter2 to bin
   AddToBin("bin",{"filter3","appsink"});
 }
//4th use D
 //              bin   itor     LIST...
 AddOne("bin","filter3",{"appsink"})
 {
   AddOne("bin","filter3");  //use A  输出 add filter3 to bin
   AddToBin("bin",{"appsink"});
 }
//5th use D
 //             bin   itor     LIST...
 AddOne("bin","appsink",{})
 {
   AddOne("bin","appsink");  //use A  输出 add appsink to bin
   AddToBin("bin",{});  //list 为空,use C
 }
//6th use C
 //       bin
 AddToBIN("bin")    //use C  输出 end
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.



变参模板类