深刻理解函數模板ios
- 編譯器從函數模板經過具體類型產生不一樣的函數
編譯器會對函數模板進行兩次編譯編程
- 對模板代碼自己進行編譯(語法檢查等)
- 對參數替換後的代碼進行編譯(語法檢查等)
注意事項函數
函數模板自己不容許隱式類型轉換this
- 自動推導類型時,必須嚴格匹配
- 顯示類型指定時,可以進行隱式類型轉換
#include <iostream> using namespace std; class Test { 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 &); 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 = 0x80487e4 pd = 0x8048806 pt = 0x8048828 編譯器作了什麼? FuncI* pi = Swap; ==> 編譯器自動類型推導 ==> 生成對應的 Swap 函數 ==> 語法檢查合法後將 Swap 地址賦值給 pi
關於編譯器對函數模板進行的兩次編譯
第 1 次:對模板代碼自己進行的編譯檢查spa
#include <iostream> using namespace std; template < typename T > void Swap(T& a, T& b) { T c = a // 注意這裏!故意製造的語法錯誤 a = b; b = c; } int main() { return 0; }
輸出:【g++】 test.cpp: In function ‘void Swap(T&, T&)’: test.cpp:9: error: expected ‘,’ or ‘;’ before ‘a’
第 2 次:對參數替換後的代碼進行編譯檢查code
#include <iostream> using namespace std; class Test { private: Test(const Test& obj); // 注意這裏!將拷貝構造函數聲明爲私有 public: Test() { } }; template < typename T > void Swap(T& a, T& b) { T c = a; a = b; b = c; } typedef void(FuncT)(Test&, Test &); int main() { FuncT* pt = Swap; // 編譯器自動推導 T 爲 Test return 0; }
輸出:【g++】 test.cpp: In function ‘void Swap(T&, T&) [with T = Test]’: test.cpp:27: instantiated from here test.cpp:8: error: ‘Test::Test(const Test&)’ is private test.cpp:18: error: within this context 分析: FuncT* pt = Swap; ==> 編譯器自動類型推導, T 爲Test類型 ==> 生成對應的 Swap 函數 ==> 語法檢查拷貝構造函數爲私有,給出錯誤提示
- 函數模板能夠定義任意多個不一樣的類型參數
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);
對於多參數函數模板it
- 沒法自動推導返回值類型
- 能夠從左向右部分指定參數類型
// T1 = int, T2 = double, T3 = double int r1 = Add<int>(0.5, 0.8); // T1 = double, T2 = double, T3 = float 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);
工程中將返回值參數做爲第一個類型參數io
#include <iostream> 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 = double, T3 = float 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; cout << "r2 = " << r2 << endl; cout << "r3 = " << r3 << endl; return 0; }
輸出: r1 = 1 r2 = 1.3 r3 = 1.3
有趣的問題:
當函數重載碰見函數模板會發生什麼?編譯
函數模板能夠像普通函數同樣被重載
- C++ 編譯器優先考慮普通函數
- 若是函數模板能夠產生更好的匹配,那麼選擇模板
- 能夠經過空模板實參列表限定編譯器只匹配模板
int r1 = Max(1, 2); double r2 = Max<>(0.5, 0.8);
#include <iostream> 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; // 普通函數 Max(int, int) cout << Max<>(a, b) << endl; // 函數模板 Max<int>(int, int) cout << Max(3.0, 4.0) << endl; // 函數模板 Max<double>(double, double) cout << Max(5.0, 6.0, 7.0) << endl; // 函數模板 Max<double>(double, double, double) cout << Max('a', 100) << endl; // 普通函數 Max(int, int) return 0; }
輸出: int Max(int a, int b) 2 T Max(T a, T b) 2 T Max(T a, T b) 4 T Max(T a, T b, T c) T Max(T a, T b) T Max(T a, T b) 7 int Max(int a, int b) 100 小提示: Max('a', 100) 普通函數被調用 函數模板自己不容許隱式類型轉換,普通函數被匹配後進行隱式類型轉換
- 函數模板經過具體類型產生不一樣的函數
- 函數模板能夠定義任意多個不一樣的類型參數
- 函數模板中的返回值類型必須顯示指定
- 函數模板能夠像普通函數同樣被重載
以上內容參考狄泰軟件學院系列課程,請你們保護原創!