You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 01:24:01