精华内容
下载资源
问答
  • 可变参数模板

    2020-04-12 10:42:38
    可变参数模板 C++11引进了一个新功能:Variadic Template Function (可变参数模板)。通过可变参数模板,我们可以创建出接受任意类型、任意个数参数的函数。 如何使用 一个声明可变参数模板的简单例子: template<...

    可变参数模板

    C++11引进了一个新功能:Variadic Template Function (可变参数模板)。通过可变参数模板,我们可以创建出接受任意类型、任意个数参数的函数。

    如何使用

    一个声明可变参数模板的简单例子:

    template<typename T, typename ... Args>
    void log(T first, Args ... args);
    

    上面的函数可以接受1至多个参数, Arg... 代表着不同个数的模板参数。

    声明一个可变参数模板函数很简单,但是定义它需要一点技巧。我们不能直接访问传进的不同数量的参数。我们需要使用c++类型解析机制以及递归来实现这一点:

    template<typename T, typename ... Args>
    void log(T first, Args ... args) {
    	cout<<first<<" , ";		//打印第一个参数
    	log(args ...);			//递归
    }
    void log(){ return; };
    

    原理

    接下来让我们看看调用log时发生了什么

    log(2, 2.2, "2.2");
    

    以上代码中,三个参数的类型分别为int, double,const char*编译会根据三个参数的类型以及我们声明的可变参数模板,创建一下的可变参数函数:

    void log(int first, double b, const char *c){
    	cout<<first<<",";
    	log(b,c);
    }
    

    这样,第一个参数被打印了出来,剩余的两个参数继续传入另外的一个可变参数模板中,跟之前一样,编译器继续创建一个可变参数函数:

    void log(double first, const char*c){
    	cout<<first<<endl;
    	log(c);
    }
    

    接下来:

    void log(const char* first){
    	cout<<first<<endl;
    	log();
    }
    

    由于只有一个参数,没有多余的参数可以传递给log(),因此我们需要定义一个没有参数的log(),调用直接返回,以此来结束递归。

    展开全文
  • C++ 可变参数模板

    2021-01-07 21:31:40
    可变参数模板可变参数模板参数包示例1示例2参考资料 可变参数模板 一个可变参数模板(variadic template)就是一个接受可变数目参数的函数模板或类模板。 参数包 可变数目的参数被称为参数包(parameter packet)。 存在...

    可变参数模板

    一个可变参数模板(variadic template)就是一个接受可变数目参数的函数模板或类模板。

    参数包

    可变数目的参数被称为参数包(parameter packet)。

    存在两种参数包:模板参数包(template parameter packet),表示0个或多个模板参数;函数参数包(function parameter packet),表示0个或多个函数参数。

    采用省略号(…)的形式来指出这是一个参数包。

    在模板参数列表中,class…或typename…指出接下来的参数表示0个或多个类型的列表。一个类型名后面跟省略号(T…)表示0个或多个给定类型T的非类型参数的列表。在函数参数列表中,如果一个参数的类型是一个模板参数包,则此参数也是一个函数参数包。

    eg:

    //Args 是一个模板参数包;rest是一个函数参数包  
    //Args 表示0个或多个模板参数类型  
    //rest 表示0个或多个函数参数  
    template<typename T,typename... Args>  
    void foo(const T &t, const Args& ... rest);  
    

    声明了foo函数是一个可变参数函数模板,它有一个名为T的类型参数和一个名为Args的模板参数包。

    示例1

    template<typename T,typename... Args>
    void foo(const T &t, const Args& ... rest)
    {
    	cout << sizeof...(Args) << ends;
    	cout << sizeof...(rest) << ends;
    }
    
    void test01()
    {
    	int i = 10;
    	double d = 3.14;
    	string s = "hello";
    	foo(i, s, 42, d);//包中有3个参数
    	foo("hi");//包中有0个参数
    }
    

    结果:
    在这里插入图片描述
    编译器为foo实例化了两个不同的版本:

    void foo(const int&,const string&,const int&,const double&);
    void foo(const char[3]&);
    

    每个实例的T类型都是第一个实参的类型,剩下的是参数包。
    sizeof...运算符可以用来查看包中元素个数。

    示例2

    通过递归调用实现可变参数函数print.

    template<typename T>
    ostream& print(ostream &os,const T &t)
    {
    	return os << t;
    }
    
    template<typename T, typename... Args>
    ostream& print(ostream &os, const T &t, const Args&... rest)
    {
    	os << t << " ";
    	return print(os, rest...);
    }
    
    void test02()
    {
    	int i = 0;
    	double d = 3.14;
    	string s = "hello";
    	print(cout,i,s,42,d,'\n');	
    }
    

    结果:
    在这里插入图片描述
    第一个版本的print是一个普通的函数模板,它接受一个输出流和一个T类型,其的功能是输出T,并返回输出流的引用。其负责终止递归并打印初始调用中的最后一个实参。

    第二个版本是一个可变参数函数模板,它打印绑定到t的实参,并调用自身打印参数包中剩下的值。
    在每一次调用

    return print(os, rest...);
    

    时,rest中的第一个包中的实参被绑定到t,剩下的形成下一个print调用的参数包。
    那么,在调用

    print(cout,i,s,42,d,'\n');
    

    时,会发生如下递归调用:

    调用 t rest…
    print(cout,i,s,42,d,’\n’) i s,42,d,’\n’
    print(cout,s,42,d,’\n’) s 42,d,’\n’
    print(cout,42,d,’\n’) 42 d,’\n’
    print(cout,d,’\n’) d ‘\n’
    print(cout,’\n’) 调用普通版本

    对于最后一次调用,编译器会选择更加特化的普通版本。

    注意:普通版本和可变参数版本要在同一作用域,否则会无限递归。

    包扩展(展开)

    对于一个参数包,除了获取其大小以外,唯一能对其进行的操作就是展开(expand)它。当展开一个包时,我们要提供用于每个扩展元素的模式(pattern)。展开一个包就是将它分解成构成的元素,对每个元素应用模式,获得展开后的列表。通过在模式右边放一个省略号(…)来触发展开操作。
    eg:

    template<typename T, typename... Args> //展开Args
    ostream& print(ostream &os, const T &t, const Args&... rest) //展开rest
    

    第一个展开操作展开模板参数包,为print生成函数参数列表。第二个扩展操作出现在对print的调用中。此模式为print调用生成实参列表。

    对Args的展开中,编译器将模式const Args& 应用到模板参数包Args中的每个元素。因此,此模式的扩展结果是一个逗号分隔的0个或多个类型的列表,每个类型形如const type& 。
    例如:

    print(cout,i,s,42,d,'\n');	//参数包大小为4
    

    最后4个实参的类型和模式一起确定了未知参数的类型。此调用被实例化为:

    ostream& print(ostream &, const int&, const string&, const int&, const double&, const char&);
    

    第二个扩展发生在对print的递归调用中。在此情况下,模式是函数参数包的名字(即rest)。此模式扩展出一个由包中元素组成的、逗号分隔的列表。因此,这个调用等价于

    print(os,s,42,d,'\n');
    

    示例3

    使用较为复杂的扩展模式。

    //... print 的两个版本
    
    template<typename T>
    string ossPrint(const T &t)
    {
    	ostringstream oss;
    	oss << t;
    	return oss.str();
    };
    
    template<typename... Args>
    ostream &msg(ostream &os, const Args&... rest)
    {
    	return print(os, ossPrint(rest)...);
    }
    
    void test03()
    {
    	int i = 0;
    	double d = 3.14;
    	string s = "hello";
    	msg(cout, i, s, 42, d, '\n');
    }
    

    在这里插入图片描述
    此处使用的模式是ossPrint(rest),这个模式表示对包中的每一个元素调用ossPrint。

    注意省略号的位置。是在ossPrint(rest)…而不是ossPrint(rest…),其原因是ossPrint并不接受一个可变参数包,它只会接受一个普通类型参数。

    参考资料

    《C++ Primer 第5版》

    展开全文
  • 模板-可变参数模板展开 一、可变参数模板 C++11增强了模板功能,在C++11之前,类模板和函数模板只能含有固定数量的模板参数,现在C++11中的新特性可变参数模板允许模板定义中包含0到任意个模板参数。可变参数模板和...

    模板-可变参数模板展开

    一、可变参数模板

    C++11增强了模板功能,在C++11之前,类模板和函数模板只能含有固定数量的模板参数,现在C++11中的新特性可变参数模板允许模板定义中包含0到任意个模板参数。可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号“…”。

    省略号的作用有两个:

    • 声明一个参数包,这个参数包中可以包含0到任意个模板参数。
    • 在模板定义的右边,可以将参数包展开成一个一个独立的参数。

    二、可变参数模板函数

    1、定义

    template <class... T>
    void f(T... args){
            cout << sizeof...(args) << endl;        //打印变参的个数
    }
    f();                                            // 0
    f(1, 2);                                        // 2
    f(1, 2.5, "");                                  // 3
    

    2、模板参数包展开方式

    1)递归函数方式展开参数包

    通过递归函数展开参数包,需要提供一个参数包展开的函数和一个递归终止函数,递归终止函数正是用来终止递归的,来看下面的例子。

    #include <iostream>
    using namespace std;
    
    // 递归终止函数
    void print(){
      cout << "empty" << endl;
    }
    /*
    或者
    template <class T>
    void print(T t){
      cout << t << endl;
    }
    或者
    template<typename T,typename T1, typename T2>
    void print(T t, T1 t1){
            cout<<t<<""<<t1 <<endl;
    }
    或者
    void print(T t, T1 t1, T2 t2){
            cout<<t<<""<<t1<<""<<t2<<endl;
    }
    */
    
    // 展开函数
    template <class T, class ...Args>
    void print(T head, Args... rest){
      cout << "parameter " << head << endl;
      print(rest...);
    }
    int main(void){
      print(1,2,3,4);
      return 0;
    }
    
    

    上例会输出每一个参数,直到为空时输出empty。有两个函数,一个是递归函数,另一个是递归终止函数,参数包Args…在展开的过程中递归调用自己,每调用一次参数包中的参数就会少一个,直到所有的参数都展开为止,当没有参数时,则调用非模板函数fun终止递归过程。

    递归调用的过程如下:

    print(1,2,3,4);
    print(2,3,4);
    print(3,4);
    print(4);
    print();
    /*
    或者
    print(1,2,3,4);
    print(2,3,4);
    print(3,4);
    print(4);
    */
    

    还可以通过std::tuple和std::enable_if方式:

    template<std::size_t I = 0, typename Tuple>
    typename std::enable_if<I == std::tuple_size<Tuple>::value>::type printtp(Tuplet){
    }
    template<std::size_t I = 0, typename Tuple>
    typename std::enable_if<I < std::tuple_size<Tuple>::value>::type printtp(Tuplet){
            std::cout << std::get<I>(t) << std::endl;
            printtp<I + 1>(t);
    }
    template<typename... Args>
    void print(Args... args){
            printtp(std::make_tuple(args...));
    }
    

    在上面的代码中,通过std::enable_if来选择合适的重载函数打印可变模版参数,基本思路是先将可变模版参数转换为tuple,然后通过递增参数的索引来选择print函数,当参数的索引小于总的参数个数时,会不断取出当前索引位置的参数并输出,当参数索引等于总的参数个数时终止递归。

    2)逗号表达式和初始化列表方式展开参数包

    递归函数展开参数包是一种标准做法,也比较好理解,但也有一个缺点,就是必须有一个重载的递归终止函数,即必须有一个同名的终止函数来终止递归,这样会感觉稍有不便。有没有一种更简单的方式,直接展开参数包呢?其实还有一种方法可以不通过递归方式来展开参数包,这种方式需要借助逗号表达式和初始化列表。

    template <class ...Args>
    void expand(Args... args){
      std::initializer_list<int>{(printarg(args), 0)...};
    }
    或者
    template<typename... Args>
    void expand (Args... args){
            std::initializer_list<int>{([&]{cout << args << endl; }(), 0)...};
    }
    

    {(printarg(args),0)…}将会展开成((printarg(arg1),0),(printarg(arg2),0),(printarg(arg3),0),etc…)

    三、可变参数模板类

    2、参数包展开方式

    1)模板递归和特化方式展开参数包

    可变参数模板类的展开一般需要定义2~3个类,包括类声明和特化的模板类。如下方式定义了一个基本的可变参数模板类,这个sum类的作用是在编译期计算出参数包中参数类型的size之和,通过sum<int,double,short>::value就可以获取这3个类型的size之和为14。

    • 普通三段式
    //前向声明
    template<typename... Args>
    struct Sum;
    
    //定义
    template<typename First, typename... Rest>
    struct Sum<First, Rest...>{
            enum { value = Sum<First>::value +Sum< Rest...>::value};
    };
    
    //特化终止递归
    template<typename Last>
    struct Sum<Last>{
            enum { value = sizeof (Last) };
    };
    /*
    或者 最后2个参数结束
    template<typename First, typename Last>
    struct sum<First, Last>{
            enum{ value = sizeof(First) +sizeof(Last) };
    };
    或者 最后0个参数结束
    template<>
    struct sum<> { 
    	enum{ value = 0 }; 
    };
    */
    
    • 也可改成两段式
    //定义
    template<typename First, typename... Rest>
    struct sum{
            enum { value = Sum<First>::value+Sum< Rest...>::value };
    };
    
    //特化
    template<typename Last>
    struct sum<Last>{
            enum{ value = sizeof(Last) };
    };
    

    还可以通过std::integral_constant来消除枚举定义value,利用std::integral_constant可以获得编译期常量的特性,可以将前面的sum例子改为这样:

    // 前向声明
    template<typename... Args>
    struct sum;
    
    // 基本定义
    template<typename First, typename... Rest>
    struct sum<First, Rest...> : std::integral_constant<int, sum<First>::value + sum<Rest...>::value>
    {
    };
    // 递归终止
    template<typename Last>
    struct sum<Last> : std::integral_constant<int, sizeof(Last)>{
    };
    sum<int,double,short>::value;// 值为14
    
    展开全文
  • /********************************... ************************* 1.Variadic Templates可变参数模板 ************************* *******************************************************************************.
    /**************************************************************************************
    	*************************     1.Variadic Templates可变参数模板    *************************
    	***************************************************************************************/
    namespace BaseFeatures_NameSpace
    {
    	//此处一定要有一个无参的ShowInformation()定义或者声明在可变参数模板版的前面
    	//该函数结束编译期递归,在解包参数为0时调用
    	void ShowInformation()
    	{
    		cout << endl;
    	}
    
    	/*
    	用法:
    	hello();
    	template<typename T,typename... args>
    	hello(T InParam1,Types&... args)
    	{
    	hello(...args);//递归
    	}
    	*/
    	template<typename T, typename... Types>
    	void ShowInformation(const T& firstArg, const Types&... args)
    	{
    		cout << "编译期递归中" << endl;
    		cout << firstArg << endl;
    		ShowInformation(args...);
    	}
    
    
    	
    	template<typename... Types>
    	void ShowInformation(const Types&... args)
    	{
    		cout << "编译期递归中,我比较没存在感,所以有楼上就不调我" << endl;
    		ShowInformation(args...);
    	}
    
    	template<typename T>
    	void ShowInformation(const T& firstArg)
    	{
    		cout << "偶是非递归的额外测试,测试能不能拦截下来递归,但是失败了" << firstArg << endl;
    	}
    
    	/**************************************************************************************
    	************************************     1.1实例    ************************************
    	***************************************************************************************/
    	//利用重载解决问题
    	//数据解析成对象
    	class VarTemplateExample_Data
    	{
    		int m_iID;
    		int m_iData;
    	};
    	class VarTemplateExample
    	{
    		
    	public:
    	
    		const char* m_strInstanceName;
    		int m_InstanceID;
    		//vector<float> m_dataContainer;
    		vector<VarTemplateExample_Data> m_dataContainer;
    
    		template< typename... Types>
    		void ResolveInformation(const char* In_strInstanceName,int In_ID,  Types&... args)
    		{
    			
    			cout << "ResolveInformation (const char* In_strInstanceName, T a, const Types&... args)" << endl;
    
    			m_strInstanceName = In_strInstanceName;
    			m_InstanceID = In_ID;
    
    			ResolveInformation(args...);
    		}
    		
    
    		template<typename... Types>
    		void ResolveInformation(int In_ID, Types&... args)
    		{
    			const char* name = "DefaultName";
    			cout << "ResolveInformation(int In_ID,const Types&... args)" << endl;
    			ResolveInformation(name, In_ID, args...);
    		}
    
    		template<typename... Types>
    		void ResolveInformation(VarTemplateExample_Data& Obj,  Types&... args)
    		{
    			
    			cout << "ResolveInformationVarTemplateExample_Data Obj, const Types&... args" << endl;
    			m_dataContainer.push_back(Obj);
    			ResolveInformation( args...);
    		}
    		//只有通过特别指定的方式才能被调用
    		template<typename T>
    		void ResolveInformation(VarTemplateExample_Data& firstArg)
    		{
    			cout << "ResolveInformation(const T& firstArg)" << endl;
    			cout << "偶是非递归的额外测试,测试能不能拦截下来递归,但是失败了"  << endl;
    		}
    
    		void ResolveInformation()
    		{
    			cout << endl;
    		}
    	private:
    
    	};
    }
    {
    			BaseFeatures_NameSpace::VarTemplateExample temp;
    			BaseFeatures_NameSpace::VarTemplateExample temp1;
    			BaseFeatures_NameSpace::VarTemplateExample temp2;
    			BaseFeatures_NameSpace::VarTemplateExample temp3;
    			BaseFeatures_NameSpace::VarTemplateExample_Data obj1;
    			BaseFeatures_NameSpace::VarTemplateExample_Data obj2;
    			BaseFeatures_NameSpace::VarTemplateExample_Data obj3;
    			BaseFeatures_NameSpace::VarTemplateExample_Data obj4;
    			int i = 0;
    			const char* str = "Test1";
    			SHOW_SMALL_FUNCTION_BLOCK_LIST_TIPS("不同参数个数的")
    			temp.ResolveInformation(str,1,obj1,obj2);
    			std::cout << endl; std::cout << endl;
    
    			temp1.ResolveInformation( 1, obj1, obj2);
    			std::cout << endl; std::cout << endl;
    
    			temp2.ResolveInformation( obj1, obj2);
    			std::cout << endl; std::cout << endl;
    
    			//只有通过这种方式才会被调用
    			temp3.ResolveInformation<BaseFeatures_NameSpace::VarTemplateExample_Data&>(obj1);
    			std::cout << endl; std::cout << endl;
    
    		}

    展开全文
  • C++可变参数模板

    2019-07-12 14:14:00
    可变参数模板 原文链接: http://blog.csdn.net/xiaohu2022/article/details/69076281 普通模板只可以采取固定数量的模板参数。然而,有时候我们希望模板可以接收任意数量的模板参数,这个时候可以采用可变参数模板...
  • c++可变参数模板

    2021-01-17 18:13:29
    可变参数模板 参数包: 10)模板参数包: template<typename T,typename… Args> Args为模板参数包,class…或typename…指出接下来的参数表示零个或多个类型的列表,一个类型名后面跟一个省略号表示零个或多个...
  • c++实现可变参数模板函数 从C ++ 11开始, std :: tuple是对Modern C ++的不可思议的扩展,它提供了固定大小的异构值集合。 不幸的是,元组在以传统方式进行管理时可能有些怀疑。 但是,随后发布的C ++标准引入了...
  • C++11 可变参数模板

    2020-11-24 23:49:59
    一个可变参数模板是一个接受可变数目参数的模板函数或模板类。可变数目的参数成为参数包。存在两种参数包:模板参数包 ,表示零个或多个模板参数;函数参数包,表示零个或多个函数参数。 用一个省略号来指出一个模板...
  • 文章目录可变参数模板sizeof... 运算符编写可变参数函数模板包扩展理解包扩展转发参数包 (emplace_back实现) 可变参数模板 ​ 一个可变参数模板就是接受一个可变数目参数的模板函数或模板类。可变数目的参数被称为...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,220
精华内容 488
关键字:

可变参数模板