my_enum的基本解决方案
对于这种情况,模板模板通常是最好的选择。不幸的是,这些只适用于类型,而不适用于函数指针等非类型参数。
但是,将这些函数转换为仿函数:
template<typename T>
struct func1;
template<>
struct func1<A> {
void operator ()() {
std::cout << "func 1 A";
}
};
接下来你就可以开始了:
template <template <typename T> class F, typename... Args>
void dispatch(my_enum me, Args&&... args) {
switch (me) {
case my_enum::A: F<A>{}(std::forward<Args>(args)...); break;
case my_enum::B: F<B>{}(std::forward<Args>(args)...); break;
case my_enum::C: F<C>{}(std::forward<Args>(args)...);
}
}
使用方法:
my_enum me = my_enum::A;
dispatch<func1>(me);
std::cout << '\n';
dispatch<func2>(me);
示例:https://godbolt.org/z/c6jPxa
更进一步的泛化
如果你愿意变得“聪明”,你也可以泛化dispatch
。定义一些帮助器存根:
template <typename TEnum, TEnum... Es>
struct Tags {};
template <typename... Ts>
struct Types {};
template <typename TTags, typename TTypes>
struct Dispatch;
标签(Tags)
和类型(Types)
用于保存枚举和需要测试的类型:
template <template <typename T> class F>
using dispatch = typename Dispatch<
Tags<my_enum, my_enum::A, my_enum::B, my_enum::C>,
Types<A, B, C>
>::type<F>;
然后,您可以通过组合初始化列表、逗号运算符和三元运算符来生成该switch块。
template <typename TEnum, TEnum... ETags, typename... TTypes>
struct Dispatch<Tags<TEnum, ETags...>, Types<TTypes...>> {
template <template <typename T> class F>
struct type {
template <typename... Args>
void operator()(TEnum e, Args&&... args)
{
(void)std::initializer_list<bool> {
e == ETags ? (F<TTypes>{}(std::forward<Args>(args)...), false) : false...
};
}
};
};
使用方法:
template <template <typename T> class F>
auto dispatch = typename Dispatch<
Tags<my_enum::A, my_enum::B, my_enum::C>,
Types<A, B, C>
>::template type<F>{};
void wrapper() {
my_enum me = my_enum::A;
dispatch<func1>(me);
std::cout << '\n';
dispatch<func2>(me);
}
示例:https://godbolt.org/z/Edhbrr
对于带有返回类型的函数
如果您想让您的函数对象返回一个值,那么就需要一些技巧了,因为如果没有匹配的标签,您需要一个默认的返回值。这个解决方案在这种情况下调用F<void>{}(...)
,通过递归测试一个参数并使用剩余的参数调用分发器。
template <typename TEnum, TEnum ETag, TEnum... ETags, typename TType, typename... TTypes>
struct Dispatch<Tags<TEnum, ETag, ETags...>, Types<TType, TTypes...>> {
template <template <typename T> class F>
struct type {
template <typename... Args>
auto operator()(TEnum e, Args&&... args) -> decltype(F<TType>{}(std::forward<Args>(args)...))
{
return e == ETag
? F<TType>{}(std::forward<Args>(args)...)
: typename Dispatch<Tags<TEnum, ETags...>, Types<TTypes...>>::template type<F>{}(e, std::forward<Args>(args)...);
}
};
};
template <typename TEnum>
struct Dispatch<Tags<TEnum>, Types<>> {
template <template <typename T> class F>
struct type {
template <typename... Args>
auto operator()(TEnum e, Args&&... args) -> decltype(F<void>{}(std::forward<Args>(args)...))
{
return F<void>{}(std::forward<Args>(args)...);
}
};
};
Demo: https://godbolt.org/z/qz6r4T