特化为特定模板实参列表提供定义,或者对于类模板和变量模板,为更具体的实参模式提供定义。只有在基于类型的分派适合作为接口时才选择特化;函数特定的分派应使用普通重载,行为资格应使用概念。
全特化 #
全特化命名每个实参,并以 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 上阅读或评论(发表评论需要登录)