如何编写适用于类模板的CRTP类型特征模板特化?
针对模板类的CRTP类型特征特化问题
先看这段CRTP(奇异递归模板模式)代码,其中派生类是模板类:
template<template<typename> class DERIVED, typename T> struct crtp_base {}; template<typename T> struct derived : crtp_base<derived,T> {};
我需要定义一个**作用于类模板(而非具体实例类型)**的类型特征,用来判断某个类模板是否继承自上述crtp_base。
最初的尝试与错误
首先定义通用版本的类型特征,参数为模板模板参数:
template<template<typename> class DERIVED> struct is_crtp_based : std::false_type {};
然后尝试编写目标场景的特化:
template<template<typename> class DERIVED, typename T> struct is_crtp_based <crtp_base<DERIVED,T>> : std::true_type {};
但编译器报错了——原因很直接:is_crtp_based的模板参数要求是类模板,但crtp_base<DERIVED,T>是一个实例化后的具体类型,两者类型不匹配,无法匹配特化规则。
问题核心
如何编写正确的模板特化?更广泛地说,怎么处理以类模板为参数的类型特征?
正确的特化实现
要修正这个问题,我们需要针对类模板DERIVED本身做特化,同时通过SFINAE(C17及之前)或C20的约束来判断DERIVED的实例是否满足CRTP继承关系:
C++20 版本(用约束)
#include <type_traits> template<template<typename> class DERIVED> struct is_crtp_based : std::false_type {}; // 当DERIVED<T>继承自crtp_base<DERIVED,T>时,触发该特化 template<template<typename> class DERIVED> requires std::is_base_of_v<crtp_base<DERIVED, void>, DERIVED<void>> struct is_crtp_based<DERIVED> : std::true_type {};
C++17 及更早版本(用SFINAE)
#include <type_traits> template<template<typename> class DERIVED, typename = void> struct is_crtp_based : std::false_type {}; // 通过enable_if_t判断继承关系,满足则匹配该特化 template<template<typename> class DERIVED> struct is_crtp_based<DERIVED, std::enable_if_t<std::is_base_of_v<crtp_base<DERIVED, void>, DERIVED<void>>>> : std::true_type {};
这里用void作为测试的T是合理的——因为CRTP的继承关系是对所有T都成立的(比如derived<T>继承crtp_base<derived,T>对任意T都成立),所以只要对某一个T满足条件,就可以判定该类模板符合要求。
对比另一种可行方案
你之前用std::is_base_of直接在主模板里判断的方案是有效的:
template<template<typename> class DERIVED, typename T=void> struct is_crtp_based : std::integral_constant<bool, std::is_base_of_v<crtp_base<DERIVED,T>,DERIVED<T> >> {}; static_assert (is_crtp_based<derived>::value == true);
但如果一定要用模板特化的方式,上面的SFINAE/约束方案就是正确的修正方向。
通用思路总结
处理以类模板为参数的类型特征时,关键要区分类模板本身和类模板的实例:
- 不能直接用实例化后的具体类型去匹配期望类模板的模板参数
- 要通过实例化类模板得到具体类型,再判断该类型的属性(比如基类关系),最后用SFINAE或约束来控制特化的匹配逻辑
内容的提问来源于stack exchange,提问作者abcdefg
相关产品推荐
相关产品推荐

