如何使用std::enable_if为C++模板类Executor的Exec方法做条件重载
实现思路
- 先实现编译期类型检查traits,判断目标类型是否存在于类的模板参数包中
- 将
Exec改造为模板成员函数,通过SFINAE(替换失败不是错误)机制实现条件重载 - 类型存在时启用带回调执行的版本,不存在时启用空实现版本,避免
std::get触发编译错误
完整实现代码
首先定义类型检查traits:
#include <tuple> #include <functional> #include <type_traits> // 示例Entity类型定义 struct Entity1 {}; struct Entity2 {}; struct Entity3 {}; // 编译期判断类型T是否在参数包Pack中 template <typename T, typename... Pack> struct is_type_in_pack : std::false_type {}; template <typename T, typename First, typename... Rest> struct is_type_in_pack<T, First, Rest...> : std::conditional<std::is_same<T, First>::value, std::true_type, is_type_in_pack<T, Rest...>>::type {};
然后是改造后的Executor类:
template<typename ...T> class Executor { using TupleOfCallback = std::tuple<std::function<void(T)>...>; public: // 构造函数初始化回调tuple Executor(const std::function<void(T)> &...func) : m_Callbacks(func...) { } // 类型存在时的Exec实现 template <typename U> typename std::enable_if<is_type_in_pack<U, T...>::value, void>::type Exec(U entity) { std::get<std::function<void(U)>>(m_Callbacks)(entity); } // 类型不存在时的Exec实现 template <typename U> typename std::enable_if<!is_type_in_pack<U, T...>::value, void>::type Exec(U entity) { // 空逻辑,可根据需求自定义 } private: TupleOfCallback m_Callbacks; };
C++17简化版本
如果你的编译环境支持C++17,可以用if constexpr省略掉两个重载的写法,代码更简洁:
template<typename ...T> class Executor { using TupleOfCallback = std::tuple<std::function<void(T)>...>; public: Executor(const std::function<void(T)> &...func) : m_Callbacks(func...) {} template <typename U> void Exec(U entity) { if constexpr (is_type_in_pack<U, T...>::value) { std::get<std::function<void(U)>>(m_Callbacks)(entity); } } private: TupleOfCallback m_Callbacks; };
测试示例
int main() { // 仅注册Entity1和Entity2的回调 Executor<Entity1*, Entity2*> task( [](Entity1 *e){ /* 处理Entity1逻辑 */ }, [](Entity2 *e){ /* 处理Entity2逻辑 */ } ); Entity1 e1; Entity2 e2; Entity3 e3; task.Exec(&e1); // 执行Entity1对应的回调 task.Exec(&e2); // 执行Entity2对应的回调 task.Exec(&e3); // 走空实现,无编译错误 return 0; }
原理解释
你原来的代码编译失败是因为类实例化时,所有非模板成员函数都会被实例化,即使你不会调用Entity3对应的Exec,std::get<std::function<void(Entity3*)>>也会因为类型不存在于tuple中触发编译错误。
将Exec改为模板成员函数后,只有当你调用对应类型的Exec时才会触发函数实例化,此时SFINAE机制会自动选择符合条件的重载,类型不存在时不会实例化带std::get的版本,也就不会触发编译错误。
内容的提问来源于stack exchange,提问作者Aurelien
相关产品推荐
相关产品推荐

