1.模板觀念與函數模板

項目地址

課程內容

  • Part1 C++模板簡介(An Introduction to C++ Template)
  • Part2 泛型編程(Generic Programming)
  • Part3 容器(Containers)
  • Part4 一些進階問題(Some Advanced Topic)

Part1 C++模板簡介

  • C++模板概觀(Overview)
  • C++函數模板(Function Template)
  • C++類模板(Class Template)
  • C++操作符重載(Operator Overloading)

C++模板概觀(1)

  • 模板(Templates)是C++的一種特性,允許函數或類(對象)通過泛型(generic types)的形式表現或運行
  • 模板可以使得函數或類在對應不同的型別(types)的時候正常工作,而無需為每一個型別都寫一份代碼
  • 一個簡單的例子:
  • 如果要寫一個取兩個數中較大值的函數Max,在不使用模板的情況下,我們不得不針對不同的型別(比如int,long,char)提供每一種型別的重載:

int Max(int a, int b)
{
    return (a>b)?a:b;
}

long Max(long a, long b)
{
    return (a>b)?a:b;
}

char Max(char a, char b)
{
    return (a>b)?a:b;
}

C++模板概觀(2)

  • 一個簡單的例子(續)
  • 如果使用模板,則可以省去一堆亢余代碼,從而將函數原型縮減到非常簡介的表達:

template <typename T> T Max(T a, T b)
{
    return (a>b)?a:b;
}

  • C++主要有兩種類型的模板:
  • 類模版(Class template):使用泛型參數的類(classes with generic parameters)
  • 函數模板(Function template):使用泛型參數的函數(functions with generic parameters)

C++模板概觀(3)

  • 模板實例化
  • 模板的聲明(declaration)其實并未給出一個函數或類的完全定義(definition),只是提供了一個函數或類的語法框架(syntactical skeletion)
  • 實例化是指從模板構建出一個真正的函數或類的過程,比如
    template <typename T> struct Object {...};
    可以用來構建諸如Object<int>,Object<char>,Object<int>,Object<MyClass>等等不同型別的具體事例
  • 實例化有兩種類型:
  • 顯式實例化-在代碼中明確指定要針對哪種型別進行實例化
  • 隱式實例化-在首次使用時根據具體情況使用一種合適的型別進行實例化

C++函數模板(1)

  • 什么是函數模板?
  • 函數模板是參數化的一族函數(a family of function)
  • 通過函數模板,可以定義一系列函數,這些函數都基于同一套代碼,但是可以作用在不同型別的參數上

template <typename T>
inline T Max(
    const T& a,
    const T& b)
{
    return (a > b) ? a:b;
}

  • 定義函數模板
  • 定義一個函數模板,返回兩數中較大的那個,該函數有兩個參數:(a,b)
  • 參數型別未定,以模板參數T表示
  • 模板參數由關鍵typename引入

C++函數模板(2)

  • 定義函數模板(續)
  • 也可以使用class替代typename來定義型別參數
    template <class T> inline T Max(const T& a, const T& b) {
    ...}
  • 從語法上講使用class和使用typename沒有區別
  • 但從語義上,class可能會導致誤區,即只有類才能作為型別參數;而事實上T所表達的意思不僅僅只針對類,任何型別都可以
  • 請盡量使用typename!
  • class可以取代typename,但struct卻不可以,以下寫法語法上是錯誤的:
    //this is wrong!!!
    template <struct T> inline T Max(const T& a, const T& b)
    {
    ...
    }

C++函數模板(3)

  • 模板函數的使用
  • 調用Max,用int,float,以及std::wstring作為模板參數替換T
  • 對于不同的型別,都從模板實例化出不同的函數實體
  • 但是不可以使用不同型別的參數來調用Max,因為編譯器在編譯時已經知道Max函數需要傳遞的型別

int i = 7, j = 30;
_tprintf(TEXT("Max(i,j) = %d\n"), Max(i,j));

double f = -1.8, g = -0.9;
_tprintf(TEXT("Max(f,g) = %f\n"), Max(f,g));

std::wstring s1 = TEXT("mathematics"), s2 = TEXT("math");
_tprintf(TEXT("Max(s1,s2) = %s\n"), Max(s1,s2).c_str());

Max(i,f);//compile error: template parameter 'T' is ambiguous  

C++函數模板(4)

  • 模板實例化
  • 用具體型別替代模板參數T的過程叫做實例化(instantiation);從而產生了一個模板事例
  • 一旦使用函數模板,這種實例化過程便由編譯器自動觸發的,不需要額外去請求模板實例化
  • 如果實例化一種型別,而該型別內部并不支持函數所使用的操作,那么就會導致一個編譯錯誤,如下所示:
  • std::complex并沒有重載“>”,也就是說改型別并不支持使用“>”比較大小,而Max函數使用“>”來判斷c1,c2的大小,所以無法通過Max(c1,c2)得到預期的結果
    std::complex<int> c1(1,2), c2(15,16); //編譯錯誤!

C++函數模板(5)

  • 結論:模板被編譯了兩次
  1. 沒有實例化之前,檢查模板代碼本身是否有語法錯誤
  2. 實例化期間,檢查對模板代碼的調用是否合法

C++函數模板(6)

  • 參數推導

  • 模板參數是有傳遞給模板函數的實參決定的

  • 不允許自動型別轉換:每個T必須嚴格匹配!
    Max(1,2) //OK:兩個實參的型別都是int
    Max(1,2.0) //ERROR:第一個參數型別是int,第二個參數型別是double

    • 一般有兩種處理這種錯誤的方法:
      1. 用static_cast或強制轉換參數型別以使兩者匹配
        Max(static_cast<double>(1), 2.0)
      2. 顯式指定T的型別
        Max<double>(1,2.0)

C++函數模板(7)

  • 函數模板重載
  • 函數模板也可以像普通函數一樣被重載
  • 非模板函數可以和同名的模板函數共存
  • 編譯器通過函數模板參數推導來決定使用調用哪個重載

//普通函數
//1
inline int const& Max(const int const& a, const int const& b)

//2
template <typename T>
inline T const& Max(const T const& a, const T const& b)

//3
template <typename T>
inline T const& Max(const T const& a, const T const& b, const T const& c)

C++函數模板(8)

函數模板重載(續)

  • Max(7, 42, 68):調用接受三個參數的模板 ————3
  • Max(7.0, 42.0):調用Max<double> (參數推導) ————2
  • Max('a', 'b'):調用Max<char> (參數推導) ————2
  • Max(7, 42):調用非模板函數,參數型別為int ————1 其他因素都相同的情況下,重載裁決過程調用非模板函數,而不是從模板產生實例
  • Max<>(7, 42):調用Max<int> (參數推導) ————2 允許空模板參數列表
  • Max<double>(7, 42): 調用Max<double> (無需參數推導) ————2
  • Max('a', 42.7):調用非模版函數,參數型別為int ————1 對于型別不同的 參數只能調用非模版函數(char型別'a'和double型別42.7都將轉化為int型別)

C++函數模板(9)

  • 總結
  • 對于不同的實參型別,模板函數定義了一族函數
  • 當傳遞模板實參的時候,函數模板依據實參的型別進行實例化
  • 可以顯式指定模板的實參型別
  • 函數模板可以重載
  • 當重載函數模板時,將改變限制在:顯式指定模板參數
  • 所有的重載版本的聲明必須位于它們被調用的位置之前
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容