函数模板、类模板
一、函数模板
template<typename T> // typename/class 都可以,这两关键字,表示 T 是一种类型 // 如果有过个类型,则用 template<typename T1, typename T2> inline // 如果模板函数是内联函数,则inline的位置如下 T Sum(T& t1, T& t2) { return t1 + t2; } template<int a, int b> // 非类型模板参数 int Sum2() { return a + b; } int main() { int a = 1; int b = 2; int c1 = Sum(a, b); // 模板参数的类型,有时候是根据你提供的数据类型推断出来的 int c2 = Sum<int>(a, b); // 也可显式指定模板的参数类型 int d1 = Sum2<1, 2>(); // 非类型模板参数传值 // 也就是说,模板<>中的值都是通过调用的时候<>中的值来赋值的 int d2 = Sum2<a, 2>(); // 不可以,非类型模板参数,必须在编译的时候就给定值, // 所以里面传递的参数必须是常量,因为常量在定义的时候就会初始化好 // 小结:模板<>中的值必须是在编译的时候就知道的,比如类型 T 要是明确地类型,int a要是确切的值 system("pause"); return 0; } // 函数模板小结:
// 模板定义不会导致编译器生成代码,只有在我们调用这个函数的时候 // 使编译器会为我们实例化一个特定版本的函数之后,编译器才会生成代码 // 函数模板的定义直接放在 .h 文件中,多个cpp include 不会出现重定义
二、类模板
1、一般定义格式
template <class T1, class T2> class MyVector { public: MyVector MyFun(); void MyFun2() { // ... // 可以直接在类模板的定义中实现成员函数的定义,这样的函数被隐式声明为inline函数 } }; template <class T1, class T2> // 函数定义之前需要将模板的参数类型写在函数体前 MyVector<T1, T2> MyVector<T1, T2>::MyFun(){ // ... // 声明在类模板定义之外,有多个模板参数,都要写在<>括号内 }
2、带非类型参数的定义格式
// 非类型模板参数 template <class T, int size = 10> class MyArray { public: T arr[size]; void Fun(); }; template <class T, int size = 10> void MyArray<T, size>::Fun(); // 非类型模板参数的限制 // (1)浮点型参数不可以做非类型模板参数,float,double // (2)类类型也不可以
3、成员函数模板
不管是普通类还是模板类,它的成员函数都可以是一个函数模板,称为成员模板函数
成员模板函数不可以是虚函数,否则编译器会报错
class Element { public: template <typename T> void fun(T t) { cout << t << std::endl; } }; Element ele; ele.fun(3); // 编译器会根据这个数字来推断成员模板函数的类型
4、模板显示实例化,模板声明
主要作用:为了防止在多个.cpp 文件中都实例化相同的类模板
// 类模板的定义在一个.h 文件中 template <T> class Element; // 在多个.cpp 文件中用到了这个类模板 template Element<int>; // 只需在一个 .cpp 文件中,写上这句话 // 实例化定义只有一个 // 这句的意思是显示地定义一个该类型的模板类 extern template Element<int>; // 在其他 .cpp 文件中只需要声明模板类即可 // extern作用:不会在本文件中生成一个extern后面所表示的模板的实例化版本代码 // extern目的:告诉编译器,其他的源文件中已经有了一个该模板类的实例化版本 // 注意:显示实例化和模板类声明都是在 .cpp 文件中,而不是在 .h 文件中
// 函数模板也是同样的道理 template <T> void fum(T t1, T t2); // 定义函数模板的.h文件中 template void fum(int t1, int t2); // 一个cpp文件中 extern template void fum(int t1, int t2); //其他用到该函数模板的cpp文件中
三、一些小知识点
1、函数指针作为其他函数的参数
// 函数指针作为类型参数的情况 typedef int(*FunType)(int, int); // 定义一个函数指针的类型, FunType可以看做是一种类型 int Sum(int i, int j) { return i + j; } void testFun(int i, int j, FunType fun) { int res = fun(i, j); // 这里传递的这个fun 就是传进来的一个函数 } // 上面的testFun函数用下面的模板函数,在main函数中的调用效果都是相同的 /////// template <typename T, typename F> void testFun(const T& i, const T& j, F fun) { cout<< fun(i, j) << endl; // 这里传递的这个fun 就是传进来的一个函数 } //////// int main() { testFun(1, 2, Sum); // 将函数名传递给一个函数作为参数 return 0; }
2、默认 类模板参数 和 函数模板参数
// 假定的函数模板如下 template <typename T, typename F> void testSum(const T& t1, const T& t2, F tempfun) { cout << tempfun(t1, t2); }
// 默认函数模板参数 typedef int(*pFun)(int, int); //定义函数指针类型 int sumfun(int i, int j) // 符合该函数指针类型的函数 { return i + j; } template <typename T, typename F = pFun> // F表示一种函数指针类型 void testSum(const T& t1, const T& t2, F tempfun = sumfun) // 表示函数指针所表示的具体函数名 { cout << tempfun(t1, t2); }
// 默认类模板参数 class Element { int operator()(int i, int j) const { // 重载圆括号运算符,此处并非构造函数 // 重载圆括号运算符之后,该类的对象可以当做函数来使用 return i + j; } }; template <typename T, typename F = Element> // F表示的是一种类型 void testSum(const T& t1, const T& t2, F tempfun = Element()) // tempfun表示F类型的具体函数名 { cout << tempfun(t1, t2); }
小结:
类模板的模板参数必须用<>指定,成员函数模板或者普通函数模板的参数可以推断
3、using 定义模板别名
template<typename T> using str_map = std::map<std::string, T> // str_map 是类型别名 template<typename T> struct{ typedef std::map<std::string, T> str_map ; // typedef 定义一个类型别名,要放在一个结构体中 }; // using用于定义类型的时候, 包括了typedef 的所有功能, // using 和 typedef 定义的区别 typedef unsigned int m_int; using m_int = unsigned int; typedef int(*pf)(int, int); using pf= int(*)(int, int); str_map<string> strmap; strmap.insert({"key", "value"});