一、模板特点
- 模板只是一个框架,不可以直接使用
- 目的是为了提高代码复用性,将类型参数化
二、分类
C++提供两种模板机制:函数模板和类模板。是一种泛型编程思想。
2.1 函数模板
语法:
// 作用:建立一个通用函数,其函数返回值类型和参数类型可以不具体指定,用一个虚拟的类型来表示。 template<typename T> 函数声明或者定义
说明:
template —— 表明要创建模板。
typename —— 表示一种数据类型,可以用 class 代替, T 就是具体的名称,通常为大写字母。
函数模板使用的注意事项
- 自动类型推导,必须能推导出一致的数据类型
- 模板使用前,必须要能确定出T的类型
2.1.1 代码示例
函数模板的两种调用方式:自动类型推导 和 显式调用
#include <iostream> using namespace std; // 函数模板,提供一个通用的相加函数 template<typename T> // 声明一个模板 T add(T a, T b){ return a+b; } // 普通函数,相同逻辑代码冗余 int add(int a, int b){ return a+b; } double add(double a, double b){ return a+b; } // 模板使用前,必须要能确定出T的类型 template<typename T> void func(){ cout << "func called." << endl; } int main(int argc, char* argv[]) { // 1. T自动类型推导 cout << "==============自动类型推导================" << endl; cout << "add(int, int): " << add(2, 3) << endl; cout << "add(double, double): " << add(2.1, 3.1) << endl; // 2. 显式的指定T类型 cout << "==============显式指定类型==============" << endl; cout << "add(int, int): " << add<int>(2, 3) << endl; cout << "add(double, double): " << add<double>(2.1, 3.1) << endl; cout << "==============注意事项==============" << endl; // cout << "add(int, int): " << add(2, 3.1) << endl; // 错误,自动推导出的T类型不一致: add(int, double) // func(); // 错误,因为该函数没有使用T的地方,所以必须先指定T的类型 func<int>(); return 0; }
运行结果:
2.1.2 函数模板与普通函数的区别
主要区别是:能否发生隐式类型转换
- 普通函数可以发生隐式类型转换
- 函数模板在使用自动类型推导时不能发生隐式类型转换,使用显式指定类型时可以,建议使用第二种
#include <iostream> using namespace std; // 函数模板 template<typename T> T add(T a, T b){ return a+b; } // 普通函数 int add(int a, int b){ return a+b; } double add(double a, double b){ return a+b; } int main(int argc, char* argv[]) { int a = 10; char c = 'c'; cout << "普通函数: " << add(a, c) << endl; // cout << add(a, c) << endl; // 如果使用函数模板,编译器不知道将T该确定为 int 还是 char,编译报错 cout << "函数模板:" << add<int>(a, c) << endl; return 0; }
运行结果:
2.2.3 函数模板和普通函数的调用规则
- 规则一:如果函数模板和普通函数都可以实现,有限调用普通函数
- 规则二:可以通过空模板参数列表来强制使用函数模板
- 规则三:函数模板也能发生重载
- 规则四:如何函数模板能发生更好的匹配,优先调用函数模板
#include <iostream> using namespace std; template<typename T> void MyPrint(T a, T b) { cout << "调用函数模板." << endl; } template<typename T> // 函数模板重载 void MyPrint(T a, T b, T c) { cout << "调用函数模板重载." << endl; } void MyPrint(int a, int b) { cout << "调用普通函数." << endl; } int main(int argc, char* argv[]) { int a = 10, b = 20; MyPrint(a, b); // 满足规则一:优先调用普通函数 MyPrint<>(a, b); // 满足规则二:强制调用函数模板 MyPrint(a, b, 100); // 满足规则三 char A = 'A', B = 'B'; // 满足规则四:优先调用函数模板,因为普通函数还需要将char隐式的转为int,函数模板直接确定char MyPrint(A, B); return 0; }
运行结果:
2.2.3 函数模板的局限性
template<class T> void func(T a, T b){ a = b; } template<class T> void compare(T a, T b){ if (a == b){ //do something } }
问题1:func函数模板提供的作用是将b的值赋给a。但是,如果T传的是一个数组,就无法实现。
问题2:compare函数模板提供的作用是比较a与b的值。但是,如果T传的是类,也无法比较。
为了解决这类问题,C++提供了可以为特定的类型提供具体化的模板。
#include <iostream> #include <string> using namespace std; class Person{ public: Person(std::string name, int age){ this->m_name = name; this->m_age = age; } public: std::string m_name; int m_age; }; template<class T> bool MyCompare(T &a, T &b){ return a == b ? true : false; } // 为Person类专门提供具体化的模板,会被优先调用 template<> bool MyCompare(Person& p1, Person& p2) { if (p1.m_name == p2.m_name && p1.m_age == p2.m_age) { return true; } else { return false; } } int main(int argc, char* argv[]) { Person p1("panda", 20); Person p2("panda", 20); bool res = MyCompare(p1, p2); if (res) { std::cout << "p1 == p2" << std::endl; } else{ std::cout << "p1 != p2" << std::endl; } return 0; }