c++ - 如何简化执行模板化函数的 switch 语句?
问题描述
考虑以下程序:
#include <string_view>
#include <string>
#include <iostream>
class A;
class B;
class C;
template<typename T>
void func1();
template<>
void func1<A>() {
std::cout << "func 1 A";
}
template<>
void func1<B>(){
std::cout << "func 1 B";
}
template<>
void func1<C>() {
std::cout << "func 1 C";
}
template<typename T>
void func2();
template<>
void func2<A>() {
std::cout << "func 2 A";
}
template<>
void func2<B>(){
std::cout << "func 2 B";
}
template<>
void func2<C>() {
std::cout << "func 2 C";
}
enum class my_enum { A, B, C};
void wrapper() {
my_enum me = my_enum::A;
switch (me) {
case my_enum::A: func1<A>(); break;
case my_enum::B: func1<B>(); break;
case my_enum::C: func1<C>();
}
std::cout << '\n';
switch (me) {
case my_enum::A: return func2<A>(); break;
case my_enum::B: return func2<B>(); break;
case my_enum::C: return func2<C>();
}
}
int main() { wrapper(); }
这里A
,B
和C
是类,并且func
是一些明确专门化的模板化函数。
如您所见,以下模式:
switch(some_enum) {
case SomeEnum::A: return func<A>();
case SomeEnum::B: return func<B>();
case SomeEnum::C: return func<C>();
default:
// error handling
}
需要重复几次,但只是功能不同。我怎样才能简化它?
我唯一能想到的就是定义一个宏,这不是很漂亮。
解决方案
my_enum 的基本解决方案
模板模板通常是这种事情的方式。不幸的是,这些只存在于类型,而不是像函数指针这样的非类型参数。
但是,把这些函数变成函子:
template<typename T>
struct func1;
template<>
struct func1<A> {
void operator ()() {
std::cout << "func 1 A";
}
};
/* etc */
你很高兴:
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>, // the tags
Types<A, B, C> // their respective types
>::type<F>;
然后,您可以通过组合初始化列表、逗号运算符和三元运算符来生成该开关块。
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)...);
}
};
};
推荐阅读
- c# - 如何在 WPF 中制作图片形状的按钮?
- ssas - 如何在 MDX 中使用 OR
- kubernetes - 为什么 PodSecurityPolicy 激活后我不能使用 RKE 部署?
- ios - com.apple.root.default-qos 中的 iOS 13 SIGABRT
- c++ - 在 C++ 中使用模板检测不同的字符串文字
- php - 跳过内爆数组中的第一个键
- zsh - 使用 READNULLCMD 访问文件名(扩展名)
- python - 正则表达式在 xml 中查找缺少的结束标记
- python - 是否可以选择循环遍历 np.where?
- mysql - MySQL返回访问过一个页面但没有访问另一个页面的成员的唯一行