1.函數模闆深入了解
- 編譯器從函數模闆通過具體類型産生不同的函數
- 編譯器會對函數模闆進行兩次編譯
- 在聲明的地方對模闆代碼本身進行編譯
- 對參數替換後的代碼進行編譯(編譯時機:可能發生在定義對象或函數調用時)
- 注意事項:
- 函數模闆本身不允許隐式類型轉換
- 自動推導類型時,必須嚴格比對
- 顯示類型指定時,能夠進行隐式類型轉換
- 程式設計實驗:隐式類型轉換問題
#include <iostream>
using namespace std;
template <typename T>
T Max(T a, T b) // 模闆函數,求兩個數的最大值
{
return a > b ? a : b; // 直接傳回值,傳回對象,就像拷貝了一份傳回去一樣,傳回引用是本身。
}
int main()
{
int i = 2;
int j = 3;
// 自動推導類型:
cout << Max(i, j) << endl;
//錯誤,兩個參數類型不同,函數模闆不允許隐式類型轉換。
//cout << Max('a', 3) << endl;
// 正确,顯示指定T為int類型,可将'a'隐式轉為指定的int類型,指定傳回值類型。
cout << Max<int>('a', 3) << endl;
system("pause");
return 0;
}
- 運作結果:
C++深度解析(46)—深入了解函數模闆 - 程式設計實驗:函數模闆的本質
#include <iostream>
#include <string>
using namespace std;
class Test
{
Test(const Test &) // 隻要發生對象的拷貝,就出錯,private的
{
}
public:
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 &); // FuncD 是一個類型,而*FuncD是一個函數指針。
typedef void (FuncT)(Test &, Test &); // 函數參數是類的引用。
int main()
{
FuncI *pi = Swap;
// 編譯器自動推導T為int。當編譯到這行時,
// 發生要用一個模闆去初始化pi(一次編譯!),而pi的類型又是void (FuncI) (int &, int &) ,
// 是以此時,編譯器會用int去替換T(二次編譯),然後生成一個Swap函數并把指針指派給pi。
FuncD *pd = Swap;//編譯器自動推導T為double,編譯器生成另一個Swap函數
// 以下證明pi與pd指向的是兩個不同的函數(這種強制類型轉換用于指針類型,整數和指針類型之間。)
cout << "pi = " << reinterpret_cast<void *>(pi) << endl;
cout << "pd = " << reinterpret_cast<void *>(pd) << endl;
//cout << "pi = " << reinterpret_cast<FuncD *>(pi) << endl; // 1
//cout << "pd = " << reinterpret_cast<FuncD *>(pd) << endl; // 1
// FuncT *pt = Swap;
// 編譯器自動推導T為test。
// 但是當進行T替換時由于Swap函數内部的T c = a;(對象的拷貝)會調用Test的拷貝構造函數,
// 但被我們故意設定為private,是以編譯出錯,這個例子就是來說明,
// 第二次編譯的時候會生成另一個不同的版本的Swap函數,參數是類的引用。
// cout << "pt = " << reinterpret_cast<void*>(pt) << endl;
// 總結:函數模闆會進行兩次編譯,一次是文法類型檢查, 二次是參數替換後進行的編譯。
system("pause");
return 0;
}
- 運作結果:
C++深度解析(46)—深入了解函數模闆
2.多參數函數模闆
- 函數模闆可以定義任意多個不同的類型參數
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 = 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);
- 程式設計實驗:多參數函數模闆
#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); //從左向右指定類型,這裡隻指定傳回值類型和函數裡面的第一個參數,第二個自動推導。
float r3 = Add<float, float, float>(0.5, 0.8);// 3個類型都顯式指定
cout << "r1 = " << r1 << endl;
cout << "r2 = " << r2 << endl;
cout << "r3 = " << r2 << endl;
system("pause");
return 0;
}
- 運作結果:
C++深度解析(46)—深入了解函數模闆
3.函數模闆與重載
- 函數模闆可以像普通函數一樣被重載
- C++編譯器優先考慮普通函數
- 如果函數模闆可以産生一個更好的比對,那麼選擇模闆
- 可以通過空模闆實參清單限定編譯器隻比對模闆
- 函數模闆不允許自動類型轉換,普通函數能夠進行自動類型轉換
- 程式設計實驗:重載函數模闆
#include <iostream>
#include <string>
using namespace std;
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;
cout << Max(a, b) << endl; // 優先比對普通函數。
cout << Max<>(a, b) << endl; // 限定隻能從模闆去比對。
cout << Max(3.0, 4.0) << endl; // 從模闆去比對,因為函數沒有這個類型
cout << Max(5.0, 6.0, 7.0) << endl; // 調了兩次模闆,從模闆比對。
cout << Max('a', 100) << endl; // 普通函數(普通函數有隐式轉換),函數模闆不允許隐式轉換,是以不會比對
system("pause");
return 0;
}
- 運作結果:
C++深度解析(46)—深入了解函數模闆
4.小結
- 函數模闆通過具體類型産生不同的函數
- 函數模闆可以定義任意多個不同的類型參數
- 函數模闆中的傳回值類型必須顯示指定
- 函數模闆可以像普通函數一樣被重載
- 普通函數有隐式轉換,函數模闆不允許隐式轉換