C++ 樣板特化

最後更新: 2025-02-08 22:23:52 +0800 +0800
原文以英文撰寫。您正在閱讀AI翻譯版本。

特化會為特定的樣板引數清單提供定義;對類別與變數樣板而言,也可為更具體的引數模式提供定義。只有在以型別為基礎的分派適合作為介面時才選用特化;函式特定的分派請使用一般多載,行為資格則使用概念。

完全特化 #

完全特化會列出每個引數,並以 template <> 開始。函式、類別與變數樣板都可以完全特化。

cpp
template <class T>
struct formatter {
    static const char* name() { return "other"; }
};

template <>
struct formatter<int> {
    static const char* name() { return "int"; }
};

完全特化是獨立的實體。它不會繼承主要樣板的成員、定義或預設樣板引數;請定義它所需的一切。在每個使用該特化的轉譯單元中,第一次使用可能隱式實例化該特化之前,先宣告完全特化。

部分特化 #

類別與變數樣板可以有部分特化。部分特化會符合某種模式,編譯器透過部分排序選擇符合的最特化模式。

cpp
#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;

部分特化需要主要樣板宣告。其引數清單必須比主要樣板更具特化性,且依語言規則仍須可推導。若符合的部分特化彼此無法比較,使用時會產生歧義並導致編譯失敗。

函式樣板不能部分特化。請加入多載函式樣板、限制它們,或透過特化的類別/變數特徵進行分派。

排序與函式庫邊界 #

當呼叫端可能隱式實例化主要樣板與適用的特化時,請將它們放在標頭檔中宣告。只有當宣告在每個可能的隱式實例化點之前可見,且連結方式相應安排時,才在單一轉譯單元中定義特化。

特化特徵可以建立清楚的分派邊界:

cpp
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 上閱讀或評論(發表評論需要登入)