1. 程式人生 > >C++Primer_Chap16_模板和泛型程式設計_List05_模板特例化_筆記

C++Primer_Chap16_模板和泛型程式設計_List05_模板特例化_筆記

  編寫單一模板,使之對任何可能的模板實參都是最合適的,都能例項化,這並不總是能辦到的。當我們不能(或不希望)使用模板版本時,可以定義類或函式模板的一個特例化版本。

定義函式模板特例化

  當我們特例化一個函式模板時,必須為原模板中的每個模板引數都提供實參。為了指出我們正在例項化一個模板,應使用關鍵字template後跟一個空尖括號對(<>)

template<typename T> int compare(const T&, const T&);

template <>
int compare(const char* const &p1, const char* const &p2)
{
    return strcmp(p1, p2);
}

  當我們定義一個特例化版本時,函式引數型別必須與一個先前宣告的模板中對應的型別匹配(類型別名、模板引數型別、指標及const之間的相互作用會讓人驚訝)。上例中T為char* const。函式要求一個指向char* const型別的const版本的引用,我們在特例化版本中使用的型別是一個指向const char的const指標的引用。

函式過載和模板特例化

  當定義函式模板的特例化版本時,我們本質上接管了編譯器的工作。即,我們為原模板的一個特殊例項提供了定義。特例化的本質是例項化一個模板,而非過載它。因此,特例化不影響函式匹配。我們將一個特殊的函式定義為一個特例化版本還是一個獨立的非模板函式,會影響到函式匹配。

  為了特例化一個模板,原模板的宣告必須在作用域中。而且,在任何使用模板例項的程式碼之前,特例化版本的什麼也必須在作用域中。(模板和其特例化版本應該宣告在同一個標頭檔案中。所有同名模板的宣告應該放在前面,然後是這些模板的特例化版本

類模板特例化

  為標準庫hash模板定義一個特例化版本,可以用它來將Sales_data物件儲存到無序容器中。一個特例化hash類必須定義:

  • 一個過載的呼叫運算子,接受一個容器關鍵字型別的物件,返回一個size_t
  • 兩個型別成員,result_type和argument_type,分別呼叫運算子的返回型別和引數型別
  • 預設建構函式和拷貝賦值運算子

  在定義此特例化版本hash時,唯一複雜的地方是:必須在原模板定義所在的名稱空間中特例化它。

namespace std {

template<>
struct hash<Sales_data>
{
    typedef size_t result_type;
    typedef Sales_data argument_type;
    size_t operator()(const Sales_data &s) const;
};

size_t
hash<Sales_data>::operator()(const Sales_data &s) const
{
    return hash<string>()(s.bookNo) ^
            hash<unsigned>()(s.units_sold) ^
            hash<double>()(s.revenue);
}

}

  由於hash使用了Sales_data的私有成員,所以必須宣告為Sales_data的友元:

template <class T> class std::hash;
class Sales_data{
friend class std::hash<Sales_data>;
};

類模板部分特例化

  與函式模板不同,類模板的特例化不必為所有模板引數提供實參。我們可以只指定一部分而非所有模板引數,或是引數的一部分而非全部特性。一個類模板的部分特例化(partial specialization)本身是一個模板,使用它時使用者還必須為那些在特例化版本中未指定的模板引數提供實參。

template <class T> struct remove_reference {
    typedef T type;
};

//部分特例化版本,將用於左值引用和右值引用
template <class T> struct remove_reference<T&>
{
    typedef T type;
};

template <class T> struct remove_reference<T&&>
{
    typedef T type;
}


int i;
//decltype(42)為int,使用原始模板
remove_reference<decltype(42)>::type a;
//decltype(i)為int&,使用T&部分特例化版本
remove_reference<decltype(i)>::type b;
//decltype(std::move(i))為int&&,使用T&&部分特例化版本
remove_reference<decltype(std::move(i))>::type c;

特例化成員而不是類

  我們可以只特例化特定成員函式而不是特例化整個模板。

template <typename T> struct Foo {
    Foo(const T &t = T()) : mem(t) {}
    void Bar() { /*  */}
    T mem;
};

template<>
void Foo<int>::Bar()
{

}

  當我們使用int之外的任何型別使用Foo時,其他成員像往常一樣進行例項化。當我們用int使用Foo時,Bar之外的成員像往常一樣進行例項化;如果我們使用Foo<int>的成員Bar,則會使用我們定義的特例化版本。