泛型编程
目录
顾名思义泛型编程就是使用“泛型”的思想去写代码,这里的“泛型”指的是将数据类型作为参数传递(类型参数化);换言之 泛型编程 是 一种不考虑具体数据类型的编程方式,其典型代表就是STL(Standard Template Library 标准模板库)。
1. 如果将泛型编程的思想应用于函数中,就产生了函数模板(通用函数);
2. 同理,将泛型编程的思想应用于类中,就会产生类模板(通用类);
接下来就分别介绍这两种技术:函数模板、类模板。
函数模板(Function Template)
1、函数模板的来源
为了更加深刻的理解函数模板,我们可以用一个例子说明情况。比如,现在要交换两个变量的值,怎么做?就目前来看有2种方法,分别是 使用宏代码块 和 使用函数定义(函数重载);
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 #define SWAP(t, a, b) \ 7 do \ 8 { \ 9 t c = a; \ 10 a = b; \ 11 b = c; \ 12 }while(0) 13 14 int main() 15 { 16 int a = 10; 17 int b = 11; 18 19 SWAP(int, a, b); 20 21 cout << "a = " << a << endl; 22 cout << "b = " << b << endl; 23 24 double m = 12; 25 double n = 13; 26 27 SWAP(double, m, n); 28 29 cout << "m = " << m << endl; 30 cout << "n = " << n << endl; 31 32 string s1 = "c++"; 33 string s2 = "python"; 34 35 SWAP(string, s1, s2); 36 37 cout << "s1 = " << s1 << endl; 38 cout << "s2 = " << s2 << endl; 39 40 return 0; 41 42 } 43 /** 44 * a = 11 45 * b = 10 46 * m = 13 47 * n = 12 48 * s1 = python 49 * s2 = c++ 50 */
方法1:使用宏代码块
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 void Swap(int& a, int& b) 7 { 8 int c = a; 9 a = b; 10 b = c; 11 } 12 13 void Swap(double& a, double& b) 14 { 15 double c = a; 16 a = b; 17 b = c; 18 } 19 20 void Swap(string& a, string& b) 21 { 22 string c = a; 23 a = b; 24 b = c; 25 } 26 27 28 int main() 29 { 30 int a = 10; 31 int b = 11; 32 33 Swap(a, b); 34 35 cout << "a = " << a << endl; 36 cout << "b = " << b << endl; 37 38 double m = 12; 39 double n = 13; 40 41 Swap(m, n); 42 43 cout << "m = " << m << endl; 44 cout << "n = " << n << endl; 45 46 string s1 = "c++"; 47 string s2 = "python"; 48 49 Swap(s1, s2); 50 51 cout << "s1 = " << s1 << endl; 52 cout << "s2 = " << s2 << endl; 53 54 return 0; 55 56 } 57 /** 58 * a = 11 59 * b = 10 60 * m = 13 61 * n = 12 62 * s1 = python 63 * s2 = c++ 64 */
方法2:使用函数定义
通过案列可知,这2种方法都可以实现功能,但是它们各自都有缺点。
定义宏代码块
优点:代码复用,适合所有的类型;
缺点:缺少类型检查;
定义函数
优点:真正的函数调用,有类型检查;
缺点:根据类型重复定义函数,无法代码复用;
那么,有没有一种方法可以同时拥有上述2种方法的优点(1. 代码复用,适合所有类型; 2. 类型检查)呢?— 当然有了,那就是 函数模板。
2、函数模板的定义
所谓函数模板,实际上是建立一个通用函数,它所用到的数据类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位);当发生函数调用时再根据传入的实参来自动推导出真正的数据类型。
在函数模板中,数据的值和类型都被参数化了,发生函数调用时编译器会根据传入的实参来推演形参的值和类型。换个角度说,函数模板除了支持值的参数化,还支持类型的参数化。
只要定义了函数模板,就可以将类型参数用于函数定义和函数声明了。
3、函数模板的特点
1. 在函数定义时可以不指明具体的数据类型;— 函数定义
2. 一种特殊的函数,可用不同的类型进行调用;— 函数调用
3. 看起来与普通函数很相似,区别是类型被参数化(当发生函数调用时,数据的类型可以通过参数来传递,编译器根据传入的实参自动推断数据类型)。— 参数传递
4、函数模板的语法规则
template <typename T>
template 关键字用于声明开始进行泛型编程;
typename 关键字用于声明泛指类型;
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template < typename T > 7 void Swap(T& a, T& b) 8 { 9 T c = a; 10 a = b; 11 b = c; 12 } 13 14 template < typename T > 15 void Sort(T a[], int len) 16 { 17 for(int i=0; i<len-1; i++) 18 { 19 for(int j=i+1; j<len; j++) 20 { 21 if( a[i] > a[j] ) 22 { 23 Swap(a[i], a[j]); 24 } 25 } 26 } 27 } 28 29 template < typename T > 30 void Print(T a[], int len) 31 { 32 for(int i=0; i<len; i++) 33 { 34 cout << a[i] << ", "; 35 } 36 37 cout << endl; 38 } 39 40 int main() 41 { 42 int a[5] = {5, 3, 2, 4, 1}; 43 44 cout << "排序前:"; 45 Print(a, 5); 46 Sort(a, 5); 47 cout << "排序后:"; 48 Print(a, 5); 49 50 string s[5] = {"Java", "C++", "Python", "Html", "Matlab"}; 51 52 cout << "排序前:"; 53 Print(s, 5); 54 Sort(s, 5); 55 cout << "排序后:"; 56 Print(s, 5); 57 58 return 0; 59 } 60 /** 61 * 排序前:5, 3, 2, 4, 1, 62 * 排序后:1, 2, 3, 4, 5, 63 * 排序前:Java, C++, Python, Html, Matlab, 64 * 排序后:C++, Html, Java, Matlab, Python, 65 */
基于函数模板的选择排序算法实现
!!!发生函数调用时,函数模板会根据 实参 对 参数类型 进行推导,但无法自动推导返回值类型;
!!!函数模板本身不允许隐式类型转换
1. 自动推导调用方式(实参类型与泛指类型必须严格匹配,不能进行隐式类型转换);
2. 具体类型显示调用(显示的指定数据类型,可以进行隐式类型转换);
3. 自动推导 + 显示调用 二者结合(在多参数的函数模板中使用);
1 template <typename T> 2 void Swap(T& a, T& b) 3 { 4 T c = a; 5 a = b; 6 b = c; 7 } 8 9 // 测试 1 10 int a = 1; 11 int b = 2; 12 Swap(a, b); // 自动推导调用方式 13 Swap<int>(a, b); // 具体类型显示调用 14 15 // 测试 2 16 int a = 3; 17 float b = 4.5; 18 Swap(a, b); // error,a, b 数据类型不一致,若此时进行函数调用,会产生二义性(如 void Swap(int a, int b); void Swap(float a, float b); ) 19 Swap<float>(a, b); // 若指定了数据类型为 float,即 T = float,在函数调用过程中(void Swap(float a, float b); ),a 进行了隐式类型转换
6、多参数的函数模板
可以从左向右部分指定类型参数,工程中将返回值类型作为第一个类型参数(这样方便进行自动推导);
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template 7 < typename T1, typename T2, typename T3 > 8 T1 Add(T2 a, T3 b) 9 { 10 return static_cast<T1>(a + b); 11 } 12 13 int main() 14 { 15 // T1 = int, T2 = double, T3 = double 16 int r1 = Add<int>(0.5, 0.8); 17 18 // T1 = double, T2 = float, T3 = double 19 double r2 = Add<double, float>(0.5, 0.8); 20 21 // T1 = float, T2 = float, T3 = float 22 float r3 = Add<float, float, float>(0.5, 0.8); 23 24 cout << "r1 = " << r1 << endl; // r1 = 1 25 cout << "r2 = " << r2 << endl; // r2 = 1.3 26 cout << "r3 = " << r3 << endl; // r3 = 1.3 27 28 return 0; 29 }
多参数的函数模板
7、函数模板与函数重载
调用规则:
1. 函数模板可以像普通函数一样被重载;
2. C++编译器优先考虑普通函数;
3. 如果函数模板可以产生一个更好的匹配,那么选择模板;
4. 可以通过 空模板实参列表 的语法限定编译器只通过模板匹配 。// func<>(a,b);
!!!函数模板本身不允许隐式类型转换;
!!!函数模板支持完全特化(在类模板中介绍)
!!!在重载函数模板时,优先选择函数模板特化,其次才是函数重载。
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 7 template < typename T > 8 T Max(T a, T b) 9 { 10 cout << "T Max(T a, T b)" << endl; 11 12 return a > b ? a : b; 13 } 14 15 int Max(int a, int b) 16 { 17 cout << "int Max(int a, int b)" << endl; 18 19 return a > b ? a : b; 20 } 21 22 template < typename T > 23 T Max(T a, T b, T c) 24 { 25 cout << "T Max(T a, T b, T c)" << endl; 26 27 return Max(Max(a, b), c); 28 } 29 30 int main() 31 { 32 cout << Max(1, 2) << endl; // 普通函数 Max(int, int) 33 cout << Max<>(1, 2) << endl; // 函数模板 Max<int>(int, int) 34 cout << Max(3.0, 4) << endl; // 普通函数 Max(int, int) 35 cout << Max(3.0, 4.0) << endl; // 函数模板 Max<double>(double, double) 36 cout << Max(5.0, 6.0, 7.0) << endl; // 函数模板 Max<double>(double, double, double) 37 38 return 0; 39 } 40 /** 41 * int Max(int a, int b) 42 * 2 43 * T Max(T a, T b) 44 * 2 45 * int Max(int a, int b) 46 * 4 47 * T Max(T a, T b) 48 * 4 49 * T Max(T a, T b, T c) 50 * T Max(T a, T b) 51 * T Max(T a, T b) 52 * 7 53 */
函数模板与函数重载
8、函数模板的实现机制
1. 编译器从函数模板通过具体类型产生不同的函数;
2. 编译器会对函数模板进行两次编译;
(1)对模板代码本身进行编译;
(2)对参数替换后的代码进行编译;
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Test 7 { 8 //Test(const Test&); 9 public: 10 Test() 11 { 12 } 13 }; 14 15 template < typename T > 16 void Swap(T& a, T& b) 17 { 18 T c = a; // 当T=Test时,第二次编译就会调用拷贝构造函数(private),此时编译失败 19 a = b; 20 b = c; 21 } 22 23 typedef void(FuncI)(int&, int&); 24 typedef void(FuncD)(double&, double&); 25 typedef void(FuncT)(Test&, Test&); 26 27 int main() 28 { 29 FuncI* pi = Swap; // 编译器自动推导 T 为 int 30 FuncD* pd = Swap; // 编译器自动推导 T 为 double 31 FuncT* pt = Swap; // 编译器自动推导 T 为 Test 32 33 cout << "pi = " << reinterpret_cast<void*>(pi) << endl; // pi = 0x400a0a 34 cout << "pd = " << reinterpret_cast<void*>(pd) << endl; // pd = 0x400a37 35 cout << "pt = " << reinterpret_cast<void*>(pt) << endl; // pt = 0x400a70 36 37 return 0; 38 }
函数模板的本质(通过函数指针测试)
类模板(Class Template)
1、 类模板的概念和意义
在C++ 中,将模板(泛型)的思想应用于类,使得类的实现不在关注数据元素的具体类型,而只关注类所需要实现的功能,这就是c++中的的类模板。总之,类模板就是以相同的方式去处理不同类型的数据。
在STL中有很多类(数组类、链表类、Stack 类、Queue 类等),这些类主要用于存储和组织数据元素,且这些类的实现与具体的数据类型无关;这样做的好处是将统一的算法应用在不同的数据类型之间。那么,在STL中,这项技术是如何实现的?— 通过 类模板 实现。
由此可见,类模板非常适合编写数据结构的相关代码。
2、类模板的语法规则
template <typename T>
template 关键字用于声明开始进行泛型编程;
typename 关键字用于声明泛指类型;
声明的泛指类型 T 可以出现在类模板的任意地方;(以上内容与函数模板一样)
!!!在类模板创建对象时,必须显示指定具体类型,不能自动推导;可以通过使用具体类型<Type>创建对象。
1 #include <iostream> 2 3 using namespace std; 4 5 // 定义一个模板类 6 template <typename T> 7 class Demo 8 { 9 private: 10 T v; 11 public: 12 Demo(T v) 13 { 14 cout << "Demo(T v)" << endl; 15 this->v = v; 16 } 17 18 T Get() 19 { 20 return v; 21 } 22 }; 23 24 int main() 25 { 26 // 使用类模板创建对象时,必须显示指定类型!!! 27 Demo<int> d1(10); 28 cout << d1.Get() << endl; 29 30 Demo<string> d2("template test"); 31 cout << d2.Get() << endl; 32 33 return 0; 34 } 35 /** 36 * Demo(T v) 37 * 10 38 * Demo(T v) 39 * template test 40 */
类模板初体验
3、 类模板的实现机制(两次编译)
编译器对类模板的处理方式和函数模板相同,即使用相同的方式处理不同的类型,都会经过两次编译。
1. 在声明的地方对类模板代码本身进行编译;
2. 在使用的地方对参数替换后的代码进行编译;(!!!部分编译,仅仅对所调用的代码进行编译)
怎么证明类模板是使用两次编译的?
为了说明问题,可以使用字符串测试。当 两个字符串直接相加时,其实是对这两个字符串进行拼接,如”string“ + ”666“ = ”string666“;但是,当字符串与数字相加时,程序在编译时就会报错,因为在string类中没有重载这种操作。
测试流程:
首先,定义一个类模板,接下来用类模板分别创建对象;
1 template < typename T1, typename T2 > 2 class Demo 3 { 4 public: 5 Demo() 6 { 7 cout << "Demo()" << endl; 8 } 9 10 T1 joint(T1 a, T2 b) 11 { 12 cout << "T1 joint(T1 a, T2 b)" << endl; 13 return a + b; 14 } 15 };
若使用如下测试语句,程序正常执行:
1 // 第二次编译,编译对象d1对应的构造函数,此时,T1=String,T2=String 2 Demo<string, string> d1; 3 // 第二次编译,编译对象d1对应的成员函数 T1 joint(T1 a, T2 b){..} 4 cout << d1.joint("Test", "_string") << endl; 5 6 /** 7 * 运行结果 8 * Demo() 9 * T1 joint(T1 a, T2 b) 10 * Test_string 11 */
若使用如下测试语句,程序则报错:
1 // 第二次编译,编译对象d2对应的构造函数,此时,T1=String,T2=int 2 Demo<string, int> d2; 3 /* 4 第二次编译,编译对象d2对应的成员函数 5 T1 joint(T1 a, T2 b) 6 { 7 cout << "T1 joint(T1 a, T2 b)" << endl; 8 return a + b; // !!!报错 9 } 10 */ 11 cout << d2.joint("Test", 3) << endl;
报错原因:String 与 int 不能直接相加,需要重载+操作符
修改错误,添加如下代码:
1 // 如 ”abc“ + 3 = ”abcabcabc“ 2 string operator+(string &l, int r) 3 { 4 string ret = ""; 5 6 while(r) 7 { 8 ret += l; 9 r--; 10 } 11 12 return ret; 13 }
程序可以正常运行,运行结果为:
最后附上完整代码:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template < typename T1, typename T2 > 7 class Demo 8 { 9 public: 10 Demo() 11 { 12 cout << "Demo()" << endl; 13 } 14 15 T1 joint(T1 a, T2 b) 16 { 17 cout << "T1 joint(T1 a, T2 b)" << endl; 18 return a + b; 19 } 20 }; 21 22 string operator+(string &l, int r) 23 { 24 string ret = ""; 25 26 while(r) 27 { 28 ret += l; 29 r--; 30 } 31 32 return ret; 33 } 34 35 36 int main() 37 { 38 Demo<string, string> d1; 39 40 cout << d1.joint("Test", "_string") << endl; 41 42 Demo<string, int> d2; 43 44 cout << d2.joint("Test", 3) << endl; 45 46 return 0; 47 } 48 49 /** 50 * 运行结果: 51 * Demo() 52 * joint(T1 a, T2 b) 53 * Test_string 54 * Demo() 55 * joint(T1 a, T2 b) 56 * TestTestTest 57 */
类模板两次编译的案列
4、类模板在工程应用中的推荐写法
1. 类模板必须在头文件中定义(.hpp文件);
2. 类模板不能分开实现在不同的文件中(声明与定义必须在同一个文件中);
3. 类模板外部定义的成员函数需要加上模板 <> 声明;
注:以上三条规则不是 C++ 和编译器的一部分,只是工程应用里习惯这样做;这样做后,代码可维护性、扩展性都会变好,因此建议遵守这三条规则;
1 // 头文件 test.hpp 2 #ifndef _TEST_H 3 #define _TEST_H 4 5 #include <iostream> 6 #include <string> 7 8 using namespace std; 9 10 template < typename T1, typename T2 > 11 class Demo 12 { 13 public: 14 Demo(); 15 T1 joint(T1 a, T2 b); 16 }; 17 18 template < typename T1, typename T2 > 19 Demo<T1, T2>::Demo() 20 { 21 cout << "Demo()" << endl; 22 } 23 24 template < typename T1, typename T2 > 25 T1 Demo<T1, T2>::joint(T1 a, T2 b) 26 { 27 cout << "T1 joint(T1 a, T2 b)" << endl; 28 return a + b; 29 } 30 31 // 全局函数 32 string operator+(string &l, int r) 33 { 34 string ret = ""; 35 36 while(r) 37 { 38 ret += l; 39 r--; 40 } 41 42 return ret; 43 } 44 45 #endif 46 47 // main.cpp 48 49 #include <iostream> 50 #include <string> 51 #include "test.hpp" 52 53 using namespace std; 54 55 int main() 56 { 57 Demo<string, string> d1; 58 59 cout << d1.joint("Test", "_string") << endl; 60 61 Demo<string, int> d2; 62 63 cout << d2.joint("Test", 3) << endl; 64 65 return 0; 66 } 67 68 /** 69 * 运行结果: 70 * Demo() 71 * joint(T1 a, T2 b) 72 * Test_string 73 * Demo() 74 * joint(T1 a, T2 b) 75 * TestTestTest 76 */
类模板的推荐写法
5、类模板的特化
“特化” 由两部分组成:
1. 部分特化:任何针对模版参数进一步进行条件限制设计的特化版本;(模板条件<>中必须有 泛指类型T,可以与其它类型一起使用)
2. 完全特化:针对所有的模版参数进行特化;(模板条件<>中都是具体类型,不包括 泛指类型T)
类模板“特化”的注意事项:
1. “特化” 就是指定类模板的特定实现;
2. “特化” 是根据类型参数分开实现类模板,本质还是同一个类模板;
3. “特化”类模板的使用方式是统一的,必须显示的指定每一个类型参数。
类模板的特化:
1 // 类模板 2 template<typename T1, typename T2> 3 class Demo{}; 4 5 // 部分特化 6 template<typename T, typename int> 7 class Demo<T, int>{}; 8 9 template<typename T> 10 class Demo<T, T>{}; 11 12 template<typename T1, typename T2> 13 class Demo<T1*, T2*>{}; 14 15 // 完全特化 16 template<> 17 class Demo<int, int>{}; 18 19 template<> 20 class Demo<const char*, int>{};
补充:函数模板的特化(只支持完全特化):
// 函数模板 template <typename T> void Equal(T a, T b){}; // 函数模板的完全特化 template <> void Equal<double>(double a, double b){}; // !!!函数模板不支持部分特化
特化案列:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template 7 < typename T1, typename T2 > 8 class Demo 9 { 10 public: 11 void joint(T1 a, T2 b) 12 { 13 cout << "类模板:void joint(T1 a, T2 b)" << endl; 14 cout << a << b << endl; 15 } 16 }; 17 18 template 19 <typename T1, typename T2 > 20 class Demo<T1*, T2*> // 关于指针的特化实现 21 { 22 public: 23 void joint(T1* a, T2* b) 24 { 25 cout << "部分指针特化:void joint(T1* a, T2* b)" << endl; 26 cout << *a << *b << endl; 27 } 28 }; 29 30 template 31 <typename T> 32 class Demo<T, T> // 当 Demo 类模板的两个类型参数完全相同时,使用这个实现 33 { 34 public: 35 void joint(T a, T b) 36 { 37 cout << "部分特化:void joint(T a, T b)" << endl; 38 cout << a << b << endl; 39 } 40 }; 41 42 template 43 <> 44 class Demo<char*, char*> // 当T1 = char*, T2 = char*,使用这个实现 45 { 46 public: 47 void joint(const char* a, const char* b) 48 { 49 cout << "完全指针特化:void joint(char* a, char* b)" << endl; 50 cout << a << b << endl; 51 } 52 }; 53 54 template 55 <> 56 class Demo<string, int> // 当T1 = string, T2 = int,使用这个实现 57 { 58 public: 59 void joint(string a, int b) 60 { 61 string ret = ""; 62 63 while(b) 64 { 65 ret += a; 66 b--; 67 } 68 69 cout << "完全特化:void joint(string a, int b)" << endl; 70 cout << ret << endl; 71 } 72 }; 73 74 75 int main() 76 { 77 Demo<string, string> d1; 78 d1.joint("Test", "_string"); 79 80 Demo<int, int> d2; 81 d2.joint(12, 34); 82 83 Demo<char*, char*> d3; 84 d3.joint("Test", "_char*"); 85 86 Demo<string, int> d4; 87 d4.joint("Test", 3); 88 89 Demo<int*, float*> dd; 90 int a = 13; 91 float b = 14; 92 dd.joint(&a, &b); 93 94 Demo<const char*, string> d; 95 d.joint("12", ".34"); 96 97 return 0; 98 } 99 100 /** 101 * 运行结果: 102 * 部分特化:void joint(T a, T b) 103 * Test_string 104 * 部分特化:void joint(T a, T b) 105 * 1234 106 * 完全指针特化:void joint(char* a, char* b) 107 * Test_char* 108 * 完全特化:void joint(string a, int b) 109 * TestTestTest 110 * 部分指针特化:void joint(T1* a, T2* b) 111 * 1314 112 * 类模板:void joint(T1 a, T2 b) 113 * 12.34 114 * /
类模板特化案列
由上述案列可知,对象调用对应成员函数的优先顺序为:完全特化 > 部分特化 > 类模板
6、关于特化的一些问题
1. 类模板特化与重定义的差异:
重定义:
(1) 重定义必须实现两个类模板 或者是 一个类模板加上 一个新的类;
(2) 重定义之后,二者没有任何关系
(3) 使用的时候需要考虑如何选择的问题(因为重定义之后又多了一个类,所以类模板统一的使用方式被销毁)
特化:
(1) 特化只是模板的分开实现,本质还是同一个类模板;
(2) 特化是类模板的一部分,而这一部分有自己专有的工作方式;
(3) 类模板特化后,能够以统一的方式使用类模板和特化类;(与类模板的使用方式相同)
结论:特化与重定义是实现同一功能的两种不同手段;在使用时,优先选择特化实现,只有特化解决不了才会重新考虑重定义。
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 template 7 < typename T1, typename T2 > 8 class Demo 9 { 10 public: 11 void joint(T1 a, T2 b) 12 { 13 cout << "类模板:void joint(T1 a, T2 b)" << endl; 14 cout << a << b << endl; 15 } 16 }; 17 18 // 完全特化实现 19 template 20 <> 21 class Demo<string, int> // 当T1 = string, T2 = int,使用这个实现 22 { 23 public: 24 void joint(string a, int b) 25 { 26 string ret = ""; 27 28 while(b) 29 { 30 ret += a; 31 b--; 32 } 33 34 cout << "完全特化:void joint(string a, int b)" << endl; 35 cout << ret << endl; 36 } 37 }; 38 39 // 重定义实现 40 class Demo_redefine 41 { 42 public: 43 void joint(string a, int b) 44 { 45 string ret = ""; 46 47 while(b) 48 { 49 ret += a; 50 b--; 51 } 52 53 cout << "重定义:void joint(string a, int b)" << endl; 54 cout << ret << endl; 55 } 56 }; 57 58 int main() 59 { 60 // 对于同一功能的不同实现方法:特化实现 与 重定义一个新类,优先选择 特化实现 61 // 特化实现 62 Demo<string, int> d4; 63 d4.joint("Test", 3); 64 // 重定义实现 65 Demo_redefine dr; 66 dr.joint("Test", 3); 67 68 return 0; 69 } 70 71 /** 72 * 运行结果: 73 * 完全特化:void joint(string a, int b) 74 * TestTestTest 75 * 重定义:void joint(string a, int b) 76 * TestTestTest 77 * /
特化与重定义案列
2. 函数模板完全特化 与 函数重载
结论:
(1)函数模板特化 与 函数重载同时存在时,编译器优先选择函数重载,其次函数模板特化,最后,函数模板;
(2)工程建议:当需要重载函数模板时,优先考虑使用模板特化;当模板特化无法满足需求,再使用函数重载!
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 // 函数模板 7 template 8 < typename T > 9 bool Equal(T a, T b) 10 { 11 cout << "函数模板:bool Equal(T a, T b)" << endl; 12 13 return a == b; 14 } 15 16 // 函数模板完全特化 17 template 18 < > 19 bool Equal<double>(double a, double b) 20 { 21 const double delta = 0.00000000000001; 22 double r = a - b; 23 24 cout << "函数模板完全特化:bool Equal<double>(double a, double b)" << endl; 25 26 return (-delta < r) && (r < delta); 27 } 28 29 // 函数重载 30 bool Equal(double a, double b) 31 { 32 const double delta = 0.00000000000001; 33 double r = a - b; 34 35 cout << "函数重载:bool Equal(double a, double b)" << endl; 36 37 return (-delta < r) && (r < delta); 38 } 39 40 int main() 41 { 42 cout << Equal( 1, 1 ) << endl; // Equal<int>(int, int) 43 cout << Equal( 1.1, 1.1 ) << endl; // Equal(double, double) // 编译器优先选择普通函数 44 cout << Equal<>( 0.001, 0.001 ) << endl;// Equal<double>(double, double) 45 46 return 0; 47 } 48 // 注:不能直接使用 “=” 判断两个浮点数是否相等 49 /** 50 * 运行结果: 51 * 函数模板:bool Equal(T a, T b) 52 * 1 53 * 函数重载:bool Equal(double a, double b) 54 * 1 55 * 函数模板完全特化:bool Equal<double>(double a, double b) 56 * 1 57 * /
函数模板特化与函数重载