C++17:如何无假设检测参数包中是否存在指定模板特化?
我完全理解你的需求——不想为不同参数形式的模板(纯类型、纯非类型、混合类型)重复写检测代码,还想沿用已经在用的多继承思路。刚好可以基于这个思路扩展出通用方案,下面一步步来实现:
核心思路回顾与扩展
你原来的多继承方案通过PackIndex继承所有PackEntry<Ts>,利用重载决议判断目标类型是否存在。要扩展到任意模板特化,我们需要:
- 一个通用的 trait,能判断任意类型是否是目标模板的特化(覆盖类型、非类型、混合参数的模板)
- 把这个 trait 结合到重载决议的SFINAE逻辑中,让
check函数只匹配属于目标模板特化的PackEntry
通用特化判断Trait
首先实现一个能识别任意模板特化的 trait,覆盖各种参数形式的模板:
#include <type_traits> #include <cstddef> // 基础模板:默认不匹配 template <typename U, auto Tmpl> struct is_specialization : std::false_type {}; // 匹配纯类型参数的模板特化(比如template <typename...> class T) template <template <typename...> class Tmpl, typename... Args> struct is_specialization<Tmpl<Args...>, Tmpl> : std::true_type {}; // 匹配纯非类型参数的模板特化(比如template <std::size_t...> class T) template <template <auto...> class Tmpl, auto... Args> struct is_specialization<Tmpl<Args...>, Tmpl> : std::true_type {}; // 匹配混合类型和非类型参数的模板特化(比如template <typename..., std::size_t...> class T) template <template <typename..., auto...> class Tmpl, typename... Ts, auto... Vs> struct is_specialization<Tmpl<Ts..., Vs...>, Tmpl> : std::true_type {}; // 辅助变量模板,简化使用 template <typename U, auto Tmpl> constexpr bool is_specialization_v = is_specialization<U, Tmpl>::value;
通用模板特化检测实现
接下来把这个 trait 结合到原有的多继承方案中,修改TmplPackSearcher使其支持任意模板:
// 保留原有的PackEntry和PackIndex结构 template <class T> struct PackEntry { using type = T; }; template <class... Ts> struct PackIndex : PackEntry<Ts>... {}; // 通用的模板特化搜索器 template <auto TargetTmpl, class... Ts> struct TmplPackSearcher { // 兜底重载:当没有匹配的PackEntry时触发 static constexpr std::false_type check(...); // SFINAE启用的重载:仅当PackEntry的type是TargetTmpl的特化时有效 template <typename U> static constexpr std::true_type check(PackEntry<U>, std::enable_if_t<is_specialization_v<U, TargetTmpl>>* = nullptr); // 推导最终的检测结果类型 using type = decltype(check(PackIndex<Ts...>{})); static constexpr bool value() { return type{}; } }; // 扩展Pack类,整合普通类检测和通用模板检测 template <class... Ts> struct Pack { // 原有的普通类检测逻辑 template<class T> static constexpr bool has() { struct PackSearcher { static constexpr std::false_type check(...); static constexpr std::true_type check(PackEntry<T>); using type = decltype(check(PackIndex<Ts...>{})); static constexpr bool value() { return type{}; } }; return PackSearcher::value(); } // 通用模板特化检测方法,支持任意参数形式的模板 template <auto TargetTmpl> static constexpr bool has_tmpl() { return TmplPackSearcher<TargetTmpl, Ts...>::value(); } };
测试用例验证
我们可以用不同参数形式的模板来验证这个方案的通用性:
// 测试用的各种模板 template <std::size_t> class NonTypeTmpl {}; template <typename, typename> class TypeTmpl {}; template <typename, std::size_t> class MixedTmpl {}; int main() { // 普通类检测验证 static_assert(Pack<int, void>::has<int>()); static_assert(!Pack<int, void>::has<bool>()); // 非类型参数模板检测验证 static_assert(Pack<NonTypeTmpl<1>, int>::has_tmpl<NonTypeTmpl>()); static_assert(!Pack<int>::has_tmpl<NonTypeTmpl>()); // 类型参数模板检测验证 static_assert(Pack<TypeTmpl<int, double>, char>::has_tmpl<TypeTmpl>()); static_assert(!Pack<int>::has_tmpl<TypeTmpl>()); // 混合参数模板检测验证 static_assert(Pack<MixedTmpl<std::size_t, 42>, float>::has_tmpl<MixedTmpl>()); static_assert(!Pack<int>::has_tmpl<MixedTmpl>()); }
兼容性说明
这个方案依赖C20的特性:允许模板模板参数作为非类型模板参数(即auto Tmpl这种写法)。如果需要兼容C17或更早版本,可以通过把模板包装成辅助类型的方式变通实现,但C++20的写法最简洁直观。
内容的提问来源于stack exchange,提问作者Lukas Barth
相关产品推荐
相关产品推荐

