函数模板、类模板

当我们需要通过函数实现同一类而非同一种功能时,往往会通过一下几种方式来实现,但是他们又或多或少存在着各种各样的缺陷:
1.对函数进行重载,针对每个所需相同行为的不同类型重新实现它。
缺陷:一旦有新类型出现,就要重新添加对应函数。
代码的复用率很低
如果只是返回值类型不同,函数重载无法解决
不易维护
2.将通用的代码放在公共的基础类里面,通过继承来实现
缺陷:无法进行参数检测
代码维护困难
3.宏函数预处理程序
缺陷:宏函数无法进行参数检测
宏所带来的各种隐患易发

由此,我们引入函数模板:
函数模板:代表了一个函数家族,该函数与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型。

#define _CRT_SECURE_NO_WARNINGS 1

#include
using namespace std;

template<class T>//这里的class也可替换为typename(尽量使用typename)
T Add(T left, T right)
{
     return left + right;
}

int main()
{
     cout << Add<int> << endl;
     return 0;
}

上例是一个实现加法功能的函数模板。

即使我们不给定相应的类型,编译器也会去自动识别它的类型然后调用类型相对应的函数。
在模板定义中,模板参数不能为空
定义模板可以采用template 也可以采用template
但是我们不能用struct来替代typename。
在定义函数时候用T来定义变量a和b 显然a和b的类型也是未知的,我们只有通过实参才能确定模板形参和返回值的类型。

模板函数也可定义为内联函数,不过,inline只能放在模板参数列表之后,返回值之前。

模板形参列表不能为空。

templateT>//这里的class也可替换为typename
inline T Add(T left, T right)
{
     return left + right;
}

模板并不是类或者函数,编译器用模板产生特定类型的类或者函数,成为模板函数实例化。

模板形参只能在模板形参之后到模板声明或定义的末尾之间使用,遵循名字屏蔽规则。

类型参数:参数类型是模糊的需要通过实参对其进行实例化如上例。
非类型参数:参数的类型是确定的
在调用非类型参数的函数时需要采用引用的方式或者显式调用。

模板形参的名字在同一模板形参列表中只能使用一次。

#define _CRT_SECURE_NO_WARNINGS 1

#include
using namespace std;

template<class T>//这里的class也可替换为typename
T Add(T left, T right)
{
     cout<<typeid(left).name()<return left + right;
}

int Add(int left, int right)
{
     return left + right;
}

int main()
{
     cout << Add<int>(1, 2) << endl;
     cout << Add<>(1.5, 2.5) << endl;
     cout << Add(3, 4) << endl;
     cout << Add(5, (int)6.1) << endl;//显示类型转换
     return 0;
}

函数模板、类模板_第1张图片

非模板函数可以和一个同名的模板函数同时存在。

如果在调用的时候条件相同,那么会优先调用非模板函数并非编译器生成的函数
如果条件相同要调用模板函数必须显式调用。即Add(a, b);

显式指定一个空的模板实参列表,该语法告诉编译器只有模板才能来匹配这个调用,而且所有的模板参数都应该根据实参演绎出来,即Max<>(a,b),那么它必须去调用模板函数,并且参数类型是由实参推演而来。

模板函数不支持隐式类型转换而普通函数可以

参数推演时,编译器不会进行类型转换,不过,有两种特殊情况:
1.const类型可被非const(引用/指针)调用
2.数组类型/函数——>指针

程序在执行过程中,模板一共被编译了两次:
1.实例化之前,检查模板是否语法错误
2.实例化时,检测模板代码,是否调用有效


函数模板支持重载。

#define _CRT_SECURE_NO_WARNINGS 1

#include
using namespace std;

template<class T>//这里的class也可替换为typename
T Add(T left, T right)
{
     cout << typeid(left).name() << endl;
     return left + right;
}

template<typename T>
T Add(T first, T second, T third)
{
     cout << typeid(left).name() << endl;
     return first + second + third;
}

int main()
{
     cout << Add<int>(1, 2) << endl;
     //cout << Add<>(1.5, 2.5) << endl;
     cout << Add<int>(3, 4, 5) << endl;
     //cout << Add(5, (int)6.1) << endl;//显示类型转换
     return 0;
}

这里写图片描述


函数模板的特化

有的时候,通用的模板并不适用于某些类型,这时候就需要对函数模板进行特化。

template<typename T>
T Max(T left, T right)
{
     return left > right ? left : right;
}

int main()
{
     char* s1 = "dabc";
     char* s2 = "adac";
     cout << Max(s1, s2) << endl;
     return 0;
}

这里写图片描述

在上例中,本来的输出结果应该是dabc,但是输出的结果却是adac,这是因为这里比较的并不是两个字符串,而是两个指针的地址大小。

template<typename T>
int Max(const T left, const T right)
{
    return left > right ? left : right;
}
template<>
int Max(char *const left, char *const right)
{
    return strcmp(left, right);
}
 1.模板函数必须已经存在
 2.和已存在的模板函数的类型必须保持一致

类模板

template<typename T>
class List
{
public:
     List()
     {
          cout << "template" << endl;
     }
};

int main()
{
     List<int> l;
     return 0;
}

1、模板函数不允许有缺省参数而类模板确是支持的
2、用类模板定义对象的时候必须在类模板名之后在尖括号内指定实际的类型名

全特化
偏特化

你可能感兴趣的:(C++,C/C++学习,class,cpp)