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...
中,从而启用相应的功能。
在此基础上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);
}
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) {}
};
我想了解如何使用 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...
中,从而启用相应的功能。
在此基础上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);
}
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) {}
};