使用boost::mp11/brigand时如何判断模板参数是否为typelist
实现方案
boost::mp11和brigand确实没有提供现成的typelist判定工具,不过两个库的typelist都是固定形式的可变参数类模板实例,自己实现类型萃取的成本极低,配合库自带的元算法可以非常简洁地实现你要的分支处理逻辑。
1. 实现typelist判定萃取
两个库的核心typelist定义非常明确:
- boost::mp11的标准typelist为
boost::mp11::mp_list<Ts...> - brigand的标准typelist为
brigand::list<Ts...>
直接通过模板偏特化写判定萃取即可,甚至不需要提前包含两个库的全量头文件,只要前置声明对应模板即可:
#include <type_traits> // 前置声明两个库的list模板 namespace boost { namespace mp11 { template<class...> class mp_list; } } namespace brigand { template<class...> class list; } // 基础模板:默认判定为非typelist template<class T> struct is_typelist : std::false_type {}; // 匹配mp11的typelist template<class... Ts> struct is_typelist<boost::mp11::mp_list<Ts...>> : std::true_type {}; // 匹配brigand的typelist template<class... Ts> struct is_typelist<brigand::list<Ts...>> : std::true_type {}; // 便利性变量模板,C++17及以上可用 template<class T> constexpr bool is_typelist_v = is_typelist<T>::value;
如果后续需要支持其他自定义typelist或者两个库的其他类型集合(比如brigand::set),只要给is_typelist添加对应的偏特化即可,扩展性很好。
2. 整合到factory类的遍历逻辑
不需要手动写递归拆参数包,直接用库自带的元算法把传入的所有参数(不管是单个类型还是typelist)拍平成一个统一的扁平typelist,再统一遍历处理即可,逻辑干净且不容易出错。
mp11版本实现
#include <boost/mp11.hpp> // 辅助模板:把单个类型转为单元素mp_list,本身是typelist则直接返回 template<class T> using as_mp_list = std::conditional_t< is_typelist_v<T>, T, boost::mp11::mp_list<T> >; template<typename... Types> class factory { // 把所有传入参数转成list后拼接,再拍平嵌套的typelist using flat_types = boost::mp11::mp_flatten< boost::mp11::mp_append<as_mp_list<Types>...> >; // 统一遍历所有展开后的类型 static void init_registry() { boost::mp11::mp_for_each<flat_types>([](auto type_witness) { using CurrentType = typename decltype(type_witness)::type; // 在这里写单个类型的处理逻辑,比如注册工厂创建函数、生成类型映射等 }); } public: factory() { init_registry(); } };
brigand版本实现
逻辑和mp11版本完全一致,替换成brigand的对应API即可:
#include <brigand/brigand.hpp> template<class T> using as_brigand_list = std::conditional_t< is_typelist_v<T>, T, brigand::list<T> >; template<typename... Types> class factory { using flat_types = brigand::flatten< brigand::append<as_brigand_list<Types>...> >; static void init_registry() { brigand::for_each<flat_types>([](auto type_witness) { using CurrentType = typename decltype(type_witness)::type; // 单个类型处理逻辑 }); } public: factory() { init_registry(); } };
补充说明
- 上述实现默认支持多层嵌套typelist,比如
factory<int, mp_list<bool, brigand::list<char, double>>>会被正确展开为int、bool、char、double四个类型依次处理,如果不需要处理嵌套,去掉mp_flatten/brigand::flatten调用即可。 - 如果你的代码里用了mp11或者brigand的其他类型集合(比如mp11的mp_set、brigand::set),只要给
is_typelist增加对应偏特化就能自动适配。 - 所有你给出的使用示例(
factory<int, bool, typelist>、factory<typelist1, int, typelist2>、factory<typelist>)都可以被正确处理,不需要修改调用方代码。
内容的提问来源于stack exchange,提问作者Max Li
相关产品推荐
相关产品推荐

