如何从派生类中提取指定基类的模板参数类型
从派生类中提取模板基类的参数包并转换为tuple类型
核心思路
要从Derived中提取Base<Ts...>的参数包并转为std::tuple<Ts...>,关键是通过模板特化和SFINAE技术,匹配Derived继承的Base模板实例,进而提取参数包。之后再基于tuple实现类型谓词的计数逻辑。
第一步:提取Base<Ts...>对应的tuple类型
首先需要两个辅助工具:一个用来检测某个类型是否是Base模板的实例,另一个用来从Derived中匹配并提取目标基类的参数包。
#include <tuple> #include <type_traits> // 检测类型是否是Base模板的实例 template <typename T, template <typename...> class Base> struct is_base_instantiation : std::false_type {}; template <template <typename...> class Base, typename... Args> struct is_base_instantiation<Base<Args...>, Base> : std::true_type {}; // 核心提取类:从Derived中找到Base<Args...>基类,返回std::tuple<Args...> template <typename Derived, template <typename...> class Base, typename = void> struct ExtractBaseTuple { // 未找到匹配基类时触发编译错误 static_assert(!std::is_same_v<Derived, Derived>, "当前派生类未继承指定的Base模板实例"); }; // 特化版本:当Derived继承自Base<Args...>时生效 template <typename Derived, template <typename...> class Base, typename... Args> struct ExtractBaseTuple<Derived, Base, std::enable_if_t<std::is_base_of_v<Base<Args...>, Derived>>> { using type = std::tuple<Args...>; }; // 别名模板,简化使用 template <typename Derived, template <typename...> class Base> using ExtractBaseTuple_t = typename ExtractBaseTuple<Derived, Base>::type;
第二步:统计tuple中满足谓词的类型数量
基于提取到的tuple,我们可以用递归模板或者C++17的折叠表达式实现计数逻辑,这里提供两种方案:
方案1:递归模板(兼容C++11及以上)
// 递归统计满足谓词的类型数量 template <typename Tuple, template <typename> class Predicate> struct CountMatchingTypes : std::integral_constant<int, 0> {}; template <typename First, typename... Rest, template <typename> class Predicate> struct CountMatchingTypes<std::tuple<First, Rest...>, Predicate> : std::integral_constant<int, (Predicate<First>::value ? 1 : 0) + CountMatchingTypes<std::tuple<Rest...>, Predicate>::value> {}; // 变量模板,直接获取计数值 template <typename Tuple, template <typename> class Predicate> constexpr int CountMatchingTypes_v = CountMatchingTypes<Tuple, Predicate>::value;
方案2:折叠表达式(C++17及以上,更简洁)
// 用折叠表达式实现的计数函数 template <typename Tuple, template <typename> class Predicate> constexpr int count_matching_types() { return []<typename... Args>(std::tuple<Args...>) { return (0 + ... + (Predicate<Args>::value ? 1 : 0)); }(Tuple{}); } // 变量模板简化调用 template <typename Tuple, template <typename> class Predicate> constexpr int count_matching_types_v = count_matching_types<Tuple, Predicate>();
完整示例
// 示例基类 template <typename... Ts> struct Base {}; // 示例派生类 struct Derived : public Base<int, double, std::string, int> {}; // 示例谓词:判断类型是否为int template <typename T> struct IsInt : std::is_same<T, int> {}; int main() { // 提取tuple类型 using MyTuple = ExtractBaseTuple_t<Derived, Base>; static_assert(std::is_same_v<MyTuple, std::tuple<int, double, std::string, int>>); // 统计满足谓词的数量 constexpr int int_count = CountMatchingTypes_v<MyTuple, IsInt>; static_assert(int_count == 2); // 如果用C++17的折叠表达式版本 constexpr int int_count_v2 = count_matching_types_v<MyTuple, IsInt>; static_assert(int_count_v2 == 2); return 0; }
注意事项
- 确保
Derived是public继承Base<Ts...>,否则std::is_base_of_v会返回false,导致提取失败。如果是私有/保护继承,可以将ExtractBaseTuple声明为Derived的友元来解决。 - 如果
Derived继承了多个Base模板的实例(比如Base<int>和Base<double>),当前实现会因为SFINAE匹配歧义而编译错误,这种情况需要额外处理(比如指定优先级或者限制仅匹配一个实例)。
内容的提问来源于stack exchange,提问作者prestokeys
相关产品推荐
相关产品推荐

