1. 程式人生 > 實用技巧 >《C++ 模板的偏特化與全特化》

《C++ 模板的偏特化與全特化》

Exceptional C++ Style(Herb Sutter).中文版(劉未鵬).pdf

第7條 為什麼不特化函式模板
有一個概念,我以前對偏特化不是特別清晰明瞭,模板分成類模板和函式模板,我原來是一致看待的。但是事實上,函式模板能不能偏特化,只能過載,每個看個偏特化的函式模板其實都是過載。 函式模板並不參加過載決議,只有當主模板被選中,才有可能去選擇某個特化版本。 而且在選擇主模板的時候,編譯器並不關心特化模板。所以,如果你確實需要某個特化版本,那麼寫成普通函式參與過載決議就可以了。過載決議是編譯器的消耗,對執行期沒有影響。應當避免函式模板的特化,因為普通函式就可以替代。 如果你需要提供介面給使用者來具體實現,那麼使用Sutter的辦法。函式模板寫成一個孤立的、永遠不被特化的版本。其實現包含在一個包含靜態函式的類模板中。這樣特化、偏特化都沒有問題。 記住,特化模板函式不是一個好的選擇。

老實說,這兩個問題的真正答案可能是 “因為”。
您可以專門化成員函式模板,它必須在類之外,並且不能使用static關鍵字:

struct Myclass {
    template <class T>
    static T func() {
        T obj{};
        return obj;
    }
};

template <>
int Myclass::func<int>() { return 42; }

兩者都是語法上的原因。這只是你必須記住的事情之一。


https://www.cnblogs.com/dracohan/p/3401660.html

複雜。。沒看懂。。


C++ 模板的偏特化與全特化

Template 中的 class 和 typename

template<class T> class Test; 
template<typename T> class Test;

在模板引入 C++ 後最初定義模板的方式是用關鍵字class,表明緊跟在後面的符號是一個型別,後來為了避免在 class 在定義類的時候帶來混淆,又引入了typename關鍵字。

在模板定義語法中關鍵字 class 與 typename 的作用完全一樣,但是在使用巢狀依賴型別 (nested depended name) 時只能使用typename

關鍵字。

typedef typename T::NestType NT;

這個時候 typename 的作用就是告訴編譯器,typename 後面的字串為一個型別名稱,而不是成員函式或者成員變數,這個時候如果前面沒有 typename,編譯器沒有任何辦法知道 T::NestType 是一個型別還是一個成員名稱 (靜態資料成員或者靜態函式),所以編譯不能夠通過。

non-type template 引數

template<class T, class Ref, size_t BufSiz = 0>

class template 擁有 non-type template 引數

模板的宣告

// 類模板
template <class T1, class T2>
class A{
    T1 data1;
    T2 data2;
};

// 函式模板
template <class T>
T max(const T lhs, const T rhs){   
    return lhs > rhs ? lhs : rhs;
}

模板的特化

特化必須在同一名稱空間下進行,可以特化類模板也可以特化函式模板,但類模板可以偏特化和全特化,而函式模板只能全特化。 模板例項化時會優先匹配” 模板引數” 最相符的那個特化版本。

全特化

通過全特化一個模板,可以對一個特定引數集合自定義當前模板,類模板和函式模板都可以全特化。全特化的模板引數列表應當是空的,並且應當給出” 模板實參” 列表:

// 全特化類模板
template <>
class A<int, double>{
    int data1;
    double data2;
};

// 函式模板
template <>
int max(const int lhs, const int rhs){   
    return lhs > rhs ? lhs : rhs;
}

注意類模板的全特化時在類名後給出了” 模板實參”,但函式模板的函式名後沒有給出” 模板實參”。 這是因為編譯器根據int max(const int, const int)的函式簽名可以推匯出來它是T max(const T, const T)的特化。

但是這一過程有時是有歧義的:

template <class T>
void f(){ T d; }

template <>
void f(){ int d; }

此時編譯器不知道 f() 是從f<T>()特化來的,編譯時會有錯誤:

error: no function template matches function template specialization 'f'

這時我們便需要顯式指定” 模板實參”:

template <class T>
void f(){ T d; }

template <>
void f<int>(){ int d; }

偏特化

類似於全特化,偏特化也是為了給自定義一個引數集合的模板,但偏特化後的模板需要進一步的例項化才能形成確定的簽名,但值得注意的是函式模板不允許偏特化。偏特化也是以 template 來宣告的,需要給出剩餘的” 模板形參” 和必要的” 模板實參”。例如:

template <class T2>
class A<int, T2>{
    ...
};

所謂偏特化並不一定是對 template 引數的 T1,T2 指定某個引數值,其意思是提供另一份 template 的定義式,而其本身仍為 templatized。

template<typename T>
class c{...}; // 這個泛化版本允許接受T為任何型別

template<typename T>
class c<T*> {...};
// 這個特化版本僅適用於“T為原生指標”的情況
// “T為原生指標”便是“T為任何型別”的一個更進一步的條件限制

函式模板是不允許偏特化的,下面的宣告會編譯錯:

template <class T1, class T2>
void f(){}

template <class T2>
void f<int, T2>(){}

但函式允許過載,宣告另一個函式模板即可替代偏特化的需要:

template <class T2>
void f(){}              // 注意:這裡沒有"模板實參"