如何通过Traits在编译时确定可变参数模板的实参?
用Traits优雅控制可变参数模板的实参选择
问题背景
我们需要定义依赖若干Pump类型的Dispatcher,基础代码如下:
struct TimerPump{}; struct GuiPump{}; struct NetworkPump{}; template<class... Pumps> class DispatcherT{}; // 原始全参数版本 using Dispatcher = DispatcherT<TimerPump, GuiPump, NetworkPump>;
要求GuiPump和NetworkPump为可选参数(可任选其一、两者都选或都不选)。目前用预处理器宏可以实现,但希望改用Traits结构体控制参数开关,尝试的代码遇到了类型选择的问题:
struct Traits { static constexpr bool gui = true; static constexpr bool network = false; }; // 此处的null无法直接使用,需要解决方案 using Dispatcher = DispatcherT<TimerPump , Traits::gui ? GuiPump : null , Traits::network ? NetworkPump : null >;
解决方案1:条件类型别名(兼容C++11)
定义模板工具类,根据布尔值决定是否提供目标类型,再将结果打包传入DispatcherT:
#include <type_traits> // 条件类型选择工具:条件为true时暴露T,否则无类型 template<bool Cond, typename T> struct ConditionalType { using type = T; }; template<typename T> struct ConditionalType<false, T> {}; // 辅助别名简化使用 template<bool Cond, typename T> using ConditionalType_t = typename ConditionalType<Cond, T>::type; struct Traits { static constexpr bool gui = true; static constexpr bool network = false; }; // 必选的TimerPump + 按Traits条件添加的可选类型 using Dispatcher = DispatcherT< TimerPump, ConditionalType_t<Traits::gui, GuiPump>, ConditionalType_t<Traits::network, NetworkPump> >;
解决方案2:C++17折叠表达式+类型标识
利用C++17的if constexpr和折叠表达式,更灵活地收集可选类型:
#include <type_traits> struct Traits { static constexpr bool gui = true; static constexpr bool network = false; }; // 根据条件返回包含目标类型或空的类型标识 template<bool Cond, typename T> auto add_if() { if constexpr(Cond) { return std::type_identity<T>{}; } else { return std::type_identity<>{}; } } // 展开参数包生成最终Dispatcher using Dispatcher = DispatcherT< TimerPump, decltype(add_if<Traits::gui, GuiPump>())::type..., decltype(add_if<Traits::network, NetworkPump>())::type... >;
解决方案3:C++20模板Lambda(最简洁)
借助C++20的模板Lambda特性,实现更直观的可选类型添加逻辑:
#include <type_traits> #include <tuple> struct Traits { static constexpr bool gui = true; static constexpr bool network = false; }; // 模板Lambda:条件为true时返回含目标类型的tuple,否则返回空tuple constexpr auto add_pump = []<bool Cond, typename T>() { if constexpr(Cond) { return std::tuple<T>{}; } else { return std::tuple<>{}; } }; // 展开tuple中的类型作为DispatcherT的参数 using Dispatcher = DispatcherT< TimerPump, std::tuple_element_t<0, decltype(add_pump.template operator()<Traits::gui, GuiPump>())>..., std::tuple_element_t<0, decltype(add_pump.template operator()<Traits::network, NetworkPump>())>... >;
说明
以上方案均通过编译期模板元编程实现类型选择,完全替代预处理器宏的侵入式写法,且不同方案适配不同C++版本,可根据项目需求选择。
内容的提问来源于stack exchange,提问作者edwinc
相关产品推荐
相关产品推荐

