第20課 - 函數模板
一.C++中如何交換兩個變量的值
void swap(int& a, int& b)
{
int t = a;
a = b;
b = t;
}
void swap(float& a, float& b)
{
float t = a;
a = b;
b = t;
}
void swap(char*& a, char*& b)
{
char* t = a;
a = b;
b = t;
}
這三個函數,除了類型不同,函數體代碼完全相同。
C++強調代碼複用,如何解決這個代碼冗餘的問題?
二.泛型編程
2.1 不考慮具體數據類型的編程模式
對於Swap函數可以考慮下面泛型的寫法 void Swap(T&a, T&b)
{
T t = a;
a = b;
b = t;
}
Swap泛型寫法中T不是一個具體的數據類型,而是泛指的任意數據類型。2.2 C++中的泛型編程->函數模板
2.2.1 提供一種特殊的函數可用不同類型進行調用
2.2.2 看起來和普通函數很相似,區別是類型可被參數化
template <typename T>
void Swap(T&a, T&b)
{
T t = a;
a = b;
b = t;
}
2.2.3 函數模板的語法規則
a.template關鍵字用於聲明開始進行泛型編程
b.typename關鍵字用於聲明泛指類型
2.2.4 函數模板的應用
a.自動類型推導調用
b.具體類型顯示調用
int a = 1;
int b = 1;
Swap(a,b); -->自動類型推導:a和b均是int,因此參數T爲int
flaot fa = 3;
flaot fb = 4;
Swap<float>(fa, fb);-->顯示類型調用:用float替換參數類型T
Source Example 2.2.4:
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
template <typename T>
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
int main(int argc, char** argv) {
int a = 1;
int b = 0;
Swap (a, b);
printf("a = %d, b = %d\n", a , b );
float c = 1.2;
float d = 1.3;
Swap<float>(c, d);
printf("c = %f, d = %f\n", c , d );
char ca = 'a';
char cb = 'b';
Swap (ca, cb);
printf("ca = %c, cb = %c\n", ca , cb );
return 0;
}
2.2.5 泛型算法:
Source Example 2.2.5(選擇排序):
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
template <typename T>
void Swap(T& a, T& b)
{
T& t = a;
a = b;
b = t;
}
template <typename T>
void SelectSort(T array[], int length)
{
for (int i = 0; i < length; i++)
{
T min = array[i];
int index = i;
for (int j = i + 1; j < length; j++)
{
if (array[j] < min)
{
min = array[j];
index = j;
}
}
Swap(array[i], array[index]);
}
}
int main(int argc, char** argv) {
int array[] = {3,2,5,4,5};
SelectSort<int>(array, 5);
for (int i = 0; i < 5; i++)
{
printf("array[%d] = %d\n", i ,array[i]);
}
return 0;
}
輸出結果如下:
2.2.6 函數模板的深入理解
a.編譯器並不是把函數模板處理成能夠處理任意類型的函數
b.編譯器從函數模板通過具體類型產生不同的函數
c.編譯器會對函數模板進行兩次編譯
在聲明的地方對模板代碼本省進行編譯
在調用的地方對參數替換後的代碼進行編譯
三.函數重載遇上函數模板
3.1 函數模板可以像普通函數一樣被重載
3.1.1 C++編譯器優先考慮普通函數
3.1.2 如果函數模板可以產生一個更好的匹配,那麼選擇模板
3.1.3 可以通過空模板實參列表的語法限定編譯器只通過模板匹配
Source Example 3.1:
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int Max(int a, int b)
{
printf("nomanl!\n");
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b)
{
printf("template!\n");
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b,T c)
{
/* A,B是未知的泛型,會調用函數模板 */
return Max(Max(a,b), c);
}
int main(int argc, char** argv) {
int a = 1;
int b = 2;
/* 優先調用普通的Max函數 */
printf("%d\n", Max(a,b));
/* 通過空模板實參列表限定編譯器調用模板函數 */
printf("%d\n", Max<>(a,b));
float fa = 3;
float fb = 4;
/* 由於是浮點型,會優先匹配函數模板 */
printf("%f\n", Max(fa,fb));
char ca = 5;
char cb = 6;
char cc = 7;
printf("%d\n", (int)Max(ca, cb, cc));
/* 調用普通函數,因爲模板不允許進行類型轉換,普通函數可以 */
Max('a', 100);
return 0;
}
輸出結果如下:3.2 注意事項
3.2.1 函數模板不允許自動類型轉化
3.2.2 普通函數能夠進行自動類型轉換
3.3 函數模板可以定義任意多個不同的類型參數
但是問題多個類型參數的模板可以支持自動類型推導嗎?
當聲明的類型參數爲返回值類型時,無法進行自動類型推導。
Source Example 3.3
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
template<typename T1,typename T2,typename RT>
RT Add(T1 a,T2 b)
{
return static_cast<RT>(a + b);
}
int main(int argc, char** argv) {
/* 顯示調用,沒有問題 */
printf("%lf", Add<char, float, double>('a', 100));
/* 推導不出來返回值類型,會報錯 */
printf("%lf", Add('a', 100));
return 0;
}
3.3.1 不完美的解決方案
將返回類型參數聲明放到第一個參數位置,調用時只需要顯示聲明返回類型參數即可
Source Example 3.3.1
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
/* 返回類型要放在第一個 */
template<typename RT,typename T1,typename T2>
RT Add(T1 a,T2 b)
{
return static_cast<RT>(a + b);
}
int main(int argc, char** argv) {
/* 顯示調用,沒有問題 */
printf("%lf\n", Add<double, char, float>('a', 100));
/* 提供返回類型 */
printf("%lf", Add<double>('a', 100.0f));
return 0;
}