32、不一樣的C++系列--函式模板
阿新 • • 發佈:2019-01-05
函式模板
泛型程式設計
首先考慮一個問題:c++中有幾種交換變數的方法?
1、巨集程式碼塊:
#define SWAP(t, a, b) \
do \
{ \
t c = a; \
a = b; \
b = c; \
}while(0)
優點: 程式碼複用,適合所有的型別
缺點:編譯器不知道巨集的存在,缺少型別檢查
2、函式:
void Swap(int& a, int & b)
{
int c = a;
a = b;
b = c;
}
優點:真正的函式呼叫,編譯器對型別進行檢查
缺點:根據型別重複定義函式,無法程式碼複用
那有沒有一種方法能集合這兩種方法的優點呢?這裡,我們來學習一個新的概念:
- 泛型程式設計的概念
- 不考慮具體資料型別的程式設計方式
對於Swap函式可以考慮下面的泛型寫法
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
Swap泛型寫法中的T不是一個具體的資料型別,而是泛指任意的資料型別。
函式模板
- C++中泛型程式設計
- 函式模板
- 一種特殊的函式可用不同型別進行呼叫
- 看起來和普通函式很相似,區別是型別可被引數化
- 能夠根據實參對引數型別進行推導
- 函式模板的語法規則
- template 關鍵字用於宣告開始進行泛型程式設計
- typename關鍵字用於宣告泛指型別
- 函式模板
template<typename T>
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
- 函式模板的使用
- 自動型別推導呼叫
- 具體型別顯示呼叫
int a = 0;
int b = 1;
Swap(a, b); // 自動推導
float c = 2;
float d = 3;
Swap<float>(c, d); // 顯示呼叫
這裡舉一個例子:
#include <iostream>
#include <string>
using namespace std;
//定義函式模板
template < typename T >
void Swap(T& a, T& b)
{
T c = a;
a = b;
b = c;
}
//定義函式模板
//注意這裡顯示的指定引數型別
template < typename T >
void Sort(T a[], int len)
{
for(int i=0; i<len; i++)
{
for(int j=i; j<len; j++)
{
if( a[i] > a[j] )
{
Swap(a[i], a[j]);
}
}
}
}
//定義函式模板
//注意這裡顯示的指定引數型別
template < typename T >
void Println(T a[], int len)
{
for(int i=0; i<len; i++)
{
cout << a[i] << ", ";
}
cout << endl;
}
int main()
{
//定義一個int型別的陣列
int a[5] = {5, 3, 2, 4, 1};
//使用函式模板
//使用時注意,因為Sort和Println第二個引數指定了引數型別,
//所以一定要注意型別匹配
Println(a, 5);
Sort(a, 5);
Println(a, 5);
//定義一個string型別的陣列
string s[5] = {"Java", "C++", "Pascal", "Ruby", "Basic"};
//使用函式模板
Println(s, 5);
Sort(s, 5);
Println(s, 5);
return 0;
}
輸出結果為:
5, 3, 2, 4, 1,
1, 2, 3, 4, 5,
Java, C++, Pascal, Ruby, Basic,
Basic, C++, Java, Pascal, Ruby,
函式模板進一步理解
- 函式模板的理解
- 編譯器從函式模板通過具體型別產生不同的函式
- 編譯器會對
函式模板進行兩次編譯
- 對
模板程式碼本身
進行編譯 - 對
引數替換後的程式碼
進行編譯
- 對
- 注意事項:
- 函式模板本身不允許隱式型別轉換
- 自動推導型別時,必須嚴格匹配
- 顯示型別指定時,能夠進行隱式型別轉換
- 函式模板本身不允許隱式型別轉換
在這裡舉一個例子:
#include <iostream>
#include <string>
using namespace std;
class Test
{
public:
Test()
{
}
Test(const Test &)
{
}
};
template < typename T >
void Swap(T& a, T& b)
{
T c = a;
a = b;
b = c;
}
typedef void(FuncI)(int&, int&);
typedef void(FuncD)(double&, double&);
typedef void(FuncT)(Test&, Test&);
int main()
{
FuncI* pi = Swap; // 編譯器自動推導 T 為 int
FuncD* pd = Swap; // 編譯器自動推導 T 為 double
FuncT* pt = Swap; // 編譯器自動推導 T 為 Test
cout << "pi = " << reinterpret_cast<void*>(pi) << endl;
cout << "pd = " << reinterpret_cast<void*>(pd) << endl;
cout << "pt = " << reinterpret_cast<void*>(pt) << endl;
return 0;
}
最終的輸出結果為:
pi = 0x104506f10
pd = 0x104506f40
pt = 0x104506f80
多引數函式模板
- 函式模板可以定義任意多個不同的型別引數
template
<typename T1, typename T2, typename T3>
T1 Add(T2 a, T3 b)
{
return static_cast<T1>( a + b );
}
//呼叫
int r = Add<int, float, double>(0.5, 0.8);
- 對於多引數函式模板
- 無法自動推導返回值型別
- 可以從左向右部分指定型別引數
- 工程中將返回值引數作為第一個型別引數
// T1 = int, T2 = double, T3 = double
int r1 = Add<int>(0.5, 0.8);
// T1 = int, T2 = float, T3 = double
int r2 = Add<int, float>(0.5, 0.8);
// T1 = int, T2 = float, T3 = float
int r3 = Add<int, float, float>(0.5, 0.8);
繼續舉例子:
#include <iostream>
#include <string>
using namespace std;
template
< typename T1, typename T2, typename T3 >
T1 Add(T2 a, T3 b)
{
return static_cast<T1>(a + b);
}
int main()
{
// T1 = int, T2 = double, T3 = double
int r1 = Add<int>(0.5, 0.8);
// T1 = double, T2 = float, T3 = double
double r2 = Add<double, float>(0.5, 0.8);
// T1 = float, T2 = float, T3 = float
float r3 = Add<float, float, float>(0.5, 0.8);
cout << "r1 = " << r1 << endl; // r1 = 1
cout << "r2 = " << r2 << endl; // r2 = 1.3
cout << "r3 = " << r3 << endl; // r3 = 1.3
return 0;
}
輸出結果為:
// 返回值為int,所以強制型別轉換為1
r1 = 1
// 返回值為double,返回正常的double值
r2 = 1.3
r3 = 1.3
過載函式模板
- 函式模板可以像普通函式一樣被過載
- C++編譯器優先考慮普通函式
- 如果函式模板可以產生一個更好的匹配,那麼選擇模板
- 可以通過空模板實參列表限定編譯器只匹配模板
int r1 = Max(1, 2);
//這裡Max<>限定編譯器只匹配函式模板
double r2 = Max<>(0.5, 0.8);
舉最後一個例子:
#include <iostream>
#include <string>
using namespace std;
//定義包含2個引數的函式模板
template < typename T >
T Max(T a, T b)
{
cout << "T Max(T a, T b)" << endl;
return a > b ? a : b;
}
//定義一個普通函式,但函式名和函式模板一樣
int Max(int a, int b)
{
cout << "int Max(int a, int b)" << endl;
return a > b ? a : b;
}
//過載函式模板
template < typename T >
T Max(T a, T b, T c)
{
cout << "T Max(T a, T b, T c)" << endl;
return Max(Max(a, b), c);
}
int main()
{
int a = 1;
int b = 2;
// 普通函式 Max(int, int)
cout << Max(a, b) << endl;
// 函式模板 Max<int>(int, int)
cout << Max<>(a, b) << endl;
// 函式模板 Max<double>(double, double)
cout << Max(3.0, 4.0) << endl;
// 函式模板 Max<double>(double, double, double)
cout << Max(5.0, 6.0, 7.0) << endl;
// 普通函式 Max(int, int)
cout << Max('a', 100) << endl;
return 0;
}
輸出結果為:
// 普通函式 Max(int, int)
int Max(int a, int b)
2
// 函式模板 Max<int>(int, int)
T Max(T a, T b)
2
// 函式模板 Max<double>(double, double)
T Max(T a, T b)
4
// 函式模板 Max<double>(double, double, double)
T Max(T a, T b, T c)
T Max(T a, T b)
T Max(T a, T b)
7
// 普通函式 Max(int, int)
int Max(int a, int b)
100