C++ 条件模板成员函数

C++ conditional template member function

我想了解如何使用 std::enable_if 在 2 个函数实现之间进行选择。在这种情况下,如果类型 TupleOfCallback 不包含所有类型,它将不会编译,因为 std::get<...> 会抛出错误。

例如: Executor<Entity1*, Entity2*> task([](Entity1 *e){}, [](Entity2 *2){});

这不会编译,因为 Entity3* 不是元组的一部分。

看来我们可以在两个具有相同原型的函数之间进行选择,

void Exec(Entity3 *entity)
{
  //enabled when Entity3* is **not** in the tuple
}

void Exec(Entity3 *entity)
{
  //enabled when Entity3 is in the tuple
  std::get<std::function<void(Entity3*)>>(m_Callbacks)(entity);
}

但我不明白如何实现这个目标。

C++模板机制对我来说还是有难度,欢迎大家帮忙。

template<typename ...T>
class Executor
{
  typedef std::tuple<std::function<void(T)>...> TupleOfCallback;
public:
  Executor(const std::function<void(T)> &...func)
  {
  }

  void Exec(Entity1 *entity)
  {
    std::get<std::function<void(Entity1*)>>(m_Callbacks)(entity);
  }

  void Exec(Entity2 *entity)
  {
    std::get<std::function<void(Entity2*)>>(m_Callbacks)(entity);
  }

  void Exec(Entity3 *entity)
  {
    std::get<std::function<void(Entity3*)>>(m_Callbacks)(entity);
  }

public:
    TupleOfCallback m_Callbacks;
};

如果你能保证所有类型只出现一次,那么下面的方法应该有效:

template<typename... Ts>
class Executor {
  using TupleOfCallback = std::tuple<std::function<void(Ts)>...>;
 public:
  Executor(const std::function<void(Ts)>&... func);

  template<class E>
  std::enable_if_t<(std::is_same_v<Ts, E*> || ...)>
  Exec(E* entity) {
    std::get<std::function<void(E*)>>(m_Callbacks)(entity);
  }
  
  template<class E>
  std::enable_if_t<!(std::is_same_v<Ts, E*> || ...)>
  Exec(E* entity) 
  { }

public:
  TupleOfCallback m_Callbacks;
};

基本思路是利用fold-expression检测E*是否包含在Ts...中,从而启用相应的功能。

Demo.

在此基础上Check if parameter pack contains a type。您可以使用两个特征来 select 调用哪个方法:

#include <iostream>
#include <type_traits>

struct Entity1 {};
struct Entity2 {};
struct Entity3 {};

template<typename What, typename ... Args>
struct is_present {
    static constexpr bool value {(std::is_same_v<What, Args> || ...)};
};

template<typename T>
struct is_entity : is_present<T,Entity1,Entity2,Entity3> {};

template <typename T, typename ...Args>
struct is_present_entity {
    static constexpr bool value = is_present<T,Args...>::value && is_entity<T>::value;
};
template <typename T, typename ...Args>
struct is_not_present_entity {
    static constexpr bool value = (!is_present<T,Args...>::value) && is_entity<T>::value;
};


template<typename ...T>
class Executor
{
  
public:
    template <typename U, std::enable_if_t< is_present_entity<U,T...>::value,bool> = true>
    void Exec(U* t){
        std::cout << "foo\n";
    }
    template <typename U, std::enable_if_t< is_not_present_entity<U,T...>::value,bool> = true>
    void Exec(U* t){
        std::cout << "bar\n";
    }
};

struct foo {};

int main(void) {
   Executor<Entity1,Entity2> ex;
   Entity1 e1;
   ex.Exec(&e1);
   Entity3 e3;
   ex.Exec(&e3);
  // foo f;
  // ex.Exec(&f);
}

output:

foo
bar

另一个 C++17 选项:

template <typename T>
class ExecutorLeaf
{
public:
  std::function<void(T)> callback;

  void Exec(T entity) { callback(entity); }
};

template <typename... Ts>
class Executor : ExecutorLeaf<Ts>...
{
public:

  Executor(const std::function<void(Ts)>&...funcs) : ExecutorLeaf<Ts>{funcs}... {}

  using ExecutorLeaf<Ts>::Exec...; // C++17
  // Fallback
  template <typename T> void Exec(T) {}
};

Demo