特化會為特定的樣板引數清單提供定義;對類別與變數樣板而言,也可為更具體的引數模式提供定義。只有在以型別為基礎的分派適合作為介面時才選用特化;函式特定的分派請使用一般多載,行為資格則使用概念。
完全特化 #
完全特化會列出每個引數,並以 template <> 開始。函式、類別與變數樣板都可以完全特化。
template <class T>
struct formatter {
static const char* name() { return "other"; }
};
template <>
struct formatter<int> {
static const char* name() { return "int"; }
};完全特化是獨立的實體。它不會繼承主要樣板的成員、定義或預設樣板引數;請定義它所需的一切。在每個使用該特化的轉譯單元中,第一次使用可能隱式實例化該特化之前,先宣告完全特化。
部分特化 #
類別與變數樣板可以有部分特化。部分特化會符合某種模式,編譯器透過部分排序選擇符合的最特化模式。
#include <type_traits>
template <class T>
struct is_pointer_like : std::false_type {};
template <class T>
struct is_pointer_like<T*> : std::true_type {};
template <class T>
inline constexpr bool is_pointer_like_v = is_pointer_like<T>::value;部分特化需要主要樣板宣告。其引數清單必須比主要樣板更具特化性,且依語言規則仍須可推導。若符合的部分特化彼此無法比較,使用時會產生歧義並導致編譯失敗。
函式樣板不能部分特化。請加入多載函式樣板、限制它們,或透過特化的類別/變數特徵進行分派。
排序與函式庫邊界 #
當呼叫端可能隱式實例化主要樣板與適用的特化時,請將它們放在標頭檔中宣告。只有當宣告在每個可能的隱式實例化點之前可見,且連結方式相應安排時,才在單一轉譯單元中定義特化。
特化特徵可以建立清楚的分派邊界:
struct LocalPacket {};
template <class T>
inline constexpr bool uses_fast_path_v = false;
template <>
inline constexpr bool uses_fast_path_v<LocalPacket> = true;程式擁有 LocalPacket 與該特徵,因此契約是明確的。不要使用特化繞過存取控制或改變無關的語意。
標準函式庫則不同。除非函式庫元件另有說明,否則程式只有在宣告至少依賴一個程式定義型別,且特化符合原始樣板要求時,才可以在 namespace std 中加入標準函式庫樣板的特化。特定元件可能禁止特化或提出額外要求。部分特化仍只適用於類別與變數樣板,並遵循一般語言規則;函式樣板不能部分特化。不要推論自己可以向 std 加入多載、主要樣板或無關宣告。請優先使用有文件說明的客製化點,例如 std::hash 與 std::formatter 特化、依引數相依的 swap、ranges 客製化,或專案擁有的特徵。
評論
在 GitHub 上閱讀或評論(發表評論需要登入)