首页 > 编程知识 正文

函数模型及其应用,函数模型及其应用知识点

时间:2023-05-05 00:46:30 阅读:25136 作者:2218

函数模板基本示例函数模板(1)背景:

如果把两个数加起来,两个数是整数,我们可能会写以下函数

intadd(intI,int j ) { return i j; }如果两个数是浮点型,函数可能会这样写。

浮动添加(float j,float j ) { return i j; )2)解决方案:那么,有没有优化的方法? 有。 为了减少代码的冗馀性,函数模板将出现。

(3)关于函数模板的理解:

3.1可以理解为一个函数家族

3.2也可以理解为一个公式

)4)函数模板语法介绍:

template模板参数列表函数返回类型函数名称(模板参数列表类型参数名称(//…) )。 示例:

templatetypenameTTsub(ta,T b ) {return a b; } 4.1模板的定义以template关键字开始

4.2类型模板前面用typename限定。 因此,当您遇到typename时,您会发现它后面有一个模子。 此外,typename将替换为class,但这里的class并不代表类的含义。

templateclassTTsub(ta,T b ) {return a b; 虽然可以使用class是历史上的理由,但还是应该使用typename,而且不能使用struct。 4.3类型的模板参数t表示一种类型。 也就是说,也可以使用其他标识符,例如u等。

4.4T这个名字可以用其他任何标识符代替,不影响程序员,使用t只是一种习惯。

)5)示范范例

namespace _ name SP1//templatetypenamettemplateclassttsub (ta,T b ) ) {return a b; } intsum=_ namesp 133603360 sub (1,2 ); 根据//1和2推测t为int类型cout sum endl; double sum1=_ namesp 1:3360 sub (1.2,3.4 ); 从//1.2和3.4可以推断t为双精度型cout sum1 endl; _ namesp 1:3360 sub (1,2.5 ); //不,是矛盾的。 编译器无法推测t是int型还是double型。 函数实例化实例化:在编译过程中使用特定“类型”而不是类型模板参数类型的过程称为实例化(有些人称为代码生成器)。

编译后可以生成obj文件。 查看obj文件,可以看到具体的实例化后的函数。

在vs开发环境中,可以通过使用dumpbin工具查看obj文件来查看实际的函数原型。 (commonobjectfileformat同时使用对象文件格式)

例如,通过编写以下行代码,编译器可以实例化两个函数版本,函数版本可以显示目标文件:

_ namesp 1:3360 sub (1,2 ); _ namesp 1:3360 sub (1.1,2.2 ); 查看//obj文件显示的具体内容如下所示。

实例化函数的名称是sub和sub,不称为sub。

//编译器根据实际情况生成的函数如下。

实例化int__cdeclsubint(int,int ) double _ _ cdeclsubdouble (double )后,函数名称由三部分组成。

(1)模板名称

)2)尖括号

)3)及尖括号内的具体类型

在编译过程中,编译器将检查函数模板的函数主体部分,以确定是否可以生成函数模板的实例化过程。

总而言之,在编写类似函数模板的代码时,编译器会做两件事

第一,编译器在实例化之前检测语法是否正确,例如是否漏写了分号。

第二,在实例化过程中也要检查编译器,以确定实例化是否有效。

//注意:函数模板的实例化过程并不是通过模板生成不同的实体来处理任何类型的单一实体,至少现在不行,也许以后可以做。

模板函数的类型估计namespace _ name SP3 { templatetypenamet,typenameuautoadd(tI,U j ) ) cout ' autoadd (ti,U j ) ' endl; 返回I j; }autoadd(intI,int j ) cout ' autoadd (inti,int j ) ) endl; 返回I j; }自动估计cout _ namesp :3360 add (1,8 ) endl; 根据//1和8的值自动估计t和u,在int类型的显示中显示(2)指定显示cout _namesp3:addint,int (1,18 ) endl; //显示指定t和u为int型cout

<< _namesp3::add<double,double>(1, 8) << endl;//T和U为double类型cout << _namesp3::add<int,double>(1, 8) << endl; 空模板参数推断 (3)空模板参数推断要求编译器调用函数模板而非普通函数。cout << _namesp3::add<>(1, 8) << endl; <>代表空类型模板参数,如果没有<>,那么就会调用非类型模板参数,这和编译器的调用优先级有关,如果我们想调用函数模板实例化的函数版本,那么可以加上<>。 函数模板重载

四:函数模板重载(函数名一样,但函数返回类型或者参数类型,或者函数参数个数不一样)
4.1:函数(函数模板)名字相同,但是参数个数或者类型不同
4.2:函数模板和函数可以同时存在,此时可以把函数看成重载。
4.3:编译器会优先调用普通函数,而非函数模板。

特化

关于特化的理解,先来看一个例子说明,什么是特化,为什么需要特化。
函数模板的特化:

template <class T>T max(const T t1, const T t2){ return t1 < t2 ? t2 : t1;}

如上已有的模板定义可能在针对一个指针类型的参数时,工作将可能会不正常,具体到字符串指针类型时,可能就需要下面的特例化模板定义:

template < >const char* max(const char* t1,const char* t2){ return (strcmp(t1,t2) < 0) ? t2 : t1;}

这样才能使max(“aaa”, “bbb”);的调用更如人意(通常没有人想比较两个常量字符串存储的地址的大小)
总结:

模板的特化是在已有的通用模板不再适用于一些特殊的类型参数时,而针对这些特殊的类型参数专门实现的模板。模板的偏特化是指需要根据模板的部分参数进行特化。函数调用匹配的规则是:先精确匹配类型参数,然后匹配函数模板,最后通过参数隐式类型转换进行匹配
4.函数模板无法偏特化,但是可以用函数模板重载或者函数重载来解决 泛化 namespace _namesp5{template <typename T,typename U>void add(T i, U j){cout << "泛化版本" << endl; cout << i << endl;cout << j << endl;}}//普通的方式就是泛化版本,即,常规化,大众化。 全特化 //T = double U = doubletemplate<>//void add<double,double>(double i, double j)void add(int i, double j){cout << "函数模板全特化" << endl;cout << i + j << endl;} 偏特化 //偏特化编译失败//template<typename T>//void add<T,int>(T i, int j)//{//cout << "函数模板偏特化" << endl;// cout << i + j << endl;//}//函数模板没有偏特化

总结:函数模板的泛化,特化,一般是写在.h文件中,并且是先有泛化版本,然后才能根据泛化版本进行特定的特化版本。

函数模板缺省参数 namespace _namesp6{void add1(int i, int j){cout << i + j << endl;}typedef void(*func1)(int, int);template<typename T,typename U,typename K = func1>void sub(T a, U b, K func1 = add1){func1(a, b);}void add2(double i, double j){cout << i + j << endl;}typedef void(*func2)(double, double);template<typename K = func2,typename T, typename U>void sub1(T a, U b, K gg = add2){gg(a, b);}}

缺省参数:
6.1可以指定函数模板的类型参数
6.2如果类型参数的第一个参数为默认类型,则,后面的所有参数不必也为默认类型,这与类模板不同。

_namesp6::sub(1, 2);_namesp6::sub1(1.5, 2.7); 非类型模板参数 基本概念 namespace _namesp7{//template <typename T,typename U,int value = 100>template <typename T, typename U, typename int value = 100> //int 前面的typename画蛇添足,但是语法没毛病(typename用来修饰后面是一个类型)//template <typename T, typename U, class int value = 100> //class 不行auto add(T i,U j){return i + j + value;}//template <typename T,int value>template <typename , int >auto fun(){return 100;}} cout << _namesp7::add<int,int,10>(1, 5) << endl; cout << _namesp7::add(1, 5) << endl;//非类型模板参数,也就是说,除了有类型以外,还可以有非类型的参数,例如值等等。

注意:非类型模板参数的一些限制:浮点数,类类型等,这些是历史原因,或许未来会支持。非类型模板参数要求是constant的,因为编译器在编译时期就要知道函数模板相关的值。

奇怪语法
a):不管是类型参数还是非类型模板参数,如果代码中没有用到,则可以不写 cout << _namesp7::fun<int ,10>() << endl; b):类型前面可以增加一个typename修饰以明确标识一个类型

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。