如何减少递归可变参数继承的代码膨胀?
解决可变参数模板Base的代码膨胀问题
针对你遇到的Base<A,B,C>和Base<A,C,B>生成重复代码的问题,核心解决方案是对模板参数进行编译期标准化(排序+去重),让不同顺序的参数列表映射到同一个实例化类型,从而避免冗余代码生成。
实现思路
- 编译期对传入的类型参数列表进行去重,避免重复类型导致的冗余继承。
- 对去重后的类型列表按固定规则排序(比如基于
std::type_index的比较),确保不同顺序的输入最终得到完全一致的参数序列。 - 修改
Base模板,让它继承标准化后的参数序列对应的Base实例,这样无论输入参数顺序如何,编译器只会生成一份代码。
完整代码实现
第一步:实现编译期元工具(去重、排序、Tuple操作)
#include <type_traits> #include <tuple> #include <typeindex> #include <iostream> #include <memory> // 辅助判断:类型T的type_index是否小于U template<typename T, typename U> constexpr bool type_less() { return std::type_index(typeid(T)) < std::type_index(typeid(U)); } // 编译期Tuple截取前N个元素(C++17兼容) template<std::size_t N, typename Tuple> struct tuple_take; template<std::size_t N, typename... Ts> struct tuple_take<N, std::tuple<Ts...>> { using type = decltype(std::tuple_cat( std::declval<std::tuple<std::tuple_element_t<0, std::tuple<Ts...>>>>(), typename tuple_take<N-1, std::tuple<Ts...>>::type{} )); }; template<typename... Ts> struct tuple_take<0, std::tuple<Ts...>> { using type = std::tuple<>; }; template<std::size_t N, typename Tuple> using tuple_take_t = typename tuple_take<N, Tuple>::type; // 编译期Tuple丢弃前N个元素(C++17兼容) template<std::size_t N, typename Tuple> struct tuple_drop; template<std::size_t N, typename T, typename... Ts> struct tuple_drop<N, std::tuple<T, Ts...>> { using type = typename tuple_drop<N-1, std::tuple<Ts...>>::type; }; template<typename T, typename... Ts> struct tuple_drop<0, std::tuple<T, Ts...>> { using type = std::tuple<T, Ts...>; }; template<std::size_t N, typename Tuple> using tuple_drop_t = typename tuple_drop<N, Tuple>::type; // 编译期类型去重 template<typename... Ts> struct unique_types; template<> struct unique_types<> { using type = std::tuple<>; }; template<typename T, typename... Ts> struct unique_types<T, Ts...> { private: using rest_types = typename unique_types<Ts...>::type; static constexpr bool type_exists = (std::is_same_v<T, Ts> || ...); public: using type = std::conditional_t<type_exists, rest_types, decltype(std::tuple_cat(std::make_tuple(std::declval<T>()), rest_types{}))>; }; template<typename... Ts> using unique_types_t = typename unique_types<Ts...>::type; // 编译期类型排序(插入排序实现) template<typename... Ts> struct sorted_types; template<> struct sorted_types<> { using type = std::tuple<>; }; // 将单个类型插入已排序的类型列表 template<typename T, typename... Ts> struct insert_into_sorted { private: using sorted_tuple = typename sorted_types<Ts...>::type; template<std::size_t I = 0, typename... Us> static constexpr std::size_t find_insert_pos(std::tuple<Us...>) { if constexpr (I == sizeof...(Us)) { return I; } else if constexpr (type_less<T, std::tuple_element_t<I, std::tuple<Us...>>>()) { return I; } else { return find_insert_pos<I+1>(std::tuple<Us...>{}); } } static constexpr std::size_t insert_pos = find_insert_pos(sorted_tuple{}); public: using type = decltype(std::tuple_cat( tuple_take_t<insert_pos, sorted_tuple>{}, std::make_tuple(std::declval<T>()), tuple_drop_t<insert_pos, sorted_tuple>{} )); }; template<typename T, typename... Ts> struct sorted_types<T, Ts...> { using type = typename insert_into_sorted<T, Ts...>::type; }; // 从Tuple中提取类型列表,生成Base<Ts...> template<typename Tuple> struct tuple_to_base; template<typename... Ts> struct tuple_to_base<std::tuple<Ts...>> { using type = class Base<Ts...>; }; template<typename Tuple> using tuple_to_base_t = typename tuple_to_base<Tuple>::type;
第二步:修改Base模板,基于标准化参数列表实现
// 前置声明 template<typename... Ts> class Base; // 单参数Base特化(保留原有默认实现) template<typename T> class Base<T> { public: virtual void visit(const T& t) const { // 默认实现:可按需修改 std::cout << "Default implementation for type\n"; } }; // 多参数Base:先标准化参数,再继承 template<typename... Ts> class Base : public tuple_to_base_t<typename sorted_types<typename unique_types<Ts...>::type>::type> { public: // 导入所有visit重载 using tuple_to_base_t<typename sorted_types<typename unique_types<Ts...>::type>::type>::visit; };
第三步:验证你的使用场景
// 测试用结构体 struct A{}; struct B{}; struct C{}; // 自定义实现类 class Implementation: public Base<A,B,C> { public: void visit(const A& a) const override { std::cout << "Special implementation for type A\n"; } void visit(const B& b) const override { std::cout << "Special implementation for type B\n"; } // C类使用默认实现 }; using BaseInterface = Base<A,B,C>; void do_visit(const BaseInterface& v) { v.visit(A{}); v.visit(B{}); v.visit(C{}); } int main() { // 验证不同参数顺序的Base是同一类型 static_assert(std::is_same_v<Base<A,B,C>, Base<A,C,B>>, "Base instances should be same type"); std::unique_ptr<BaseInterface> v = std::make_unique<Implementation>(); do_visit(*v); return 0; }
效果说明
- 编译期会自动将
Base<A,B,C>、Base<A,C,B>、Base<C,A,B>等所有排列组合,统一映射为同一个实例化类型(排序后的Base<A,B,C>),彻底消除冗余代码。 - 支持自动去重,即使传入重复类型(如
Base<A,A,B>),也会自动简化为Base<A,B>。 - 完全兼容你原有的使用场景,自定义实现类无需修改,只需继承修改后的
Base即可。
内容的提问来源于stack exchange,提问作者S.Li
相关产品推荐
相关产品推荐

