c++ - 用于具有不同签名的函数的枚举切换器

标签 c++ templates casting switch-statement variadic-templates

很多时候,碰巧会发现我有一个枚举或一个整数,并且我有针对特定枚举值/整数进行参数化的函数。在运行时,我有一个枚举/整数,用于调用特定函数。通过一些代码应该会更清楚:

template<int i>
struct MyTrait
  {
  void f(int) { return 999;}
  };

template<>
struct MyTrait<1>
  {
  void f(int a, int b) { return 1; }
  };

template<>
struct MyTrait<2>
  {
  static const int ARD = 2;
  void f(int a, int b, int c) { return 100;}
  };

template<>
struct MyTrait<3>
  {
  static const int ARD = 3;
  void f(int a) { return 120;}
  };


template<class... Args>
int switcher(int value, Args... arguments)
  {
  switch(value)
    {
    case indices:
      return MyTrait<1>::f(arguments...);
    case 2:
      MyTrait<2>::f(arguments...);
    case 3:
      MyTrait<3>::f(arguments...);
    default:
       0;
    }
  return 1;
  }

// switcher(3, 10) should call MyTrait<3>::f 

要举例说明为什么这很有用,请查看 here

我正在使用 C++11,并且实现了与我发布的链接中提供的 cswitch 类似的功能。问题是,目前它仅适用于具有相同签名的函数。相反,我的目标是让它也可以与具有不同签名的函数一起使用,例如示例特征中的函数。

这里的主要目标是性能:与交换机相同的速度。 当然,如果可以在 switch 中使用 ... 运算符、返回(取决于参数)不同值的函数或者如果命名标签 gcc 扩展是标准功能(唉,我还考虑将 goto 语句作为最后的手段)。

我已经尝试了数十种方法等,但似乎没有什么能按照我想要的方式工作。我开始认为目前这是不可能的。请随意(我真的希望如此!)来反驳我。

到目前为止,我找到的最接近的解决方案是创建一个跳转表,将参数存储到一个元组中,将该元组(地址)转换为 void*,然后将其转换回特征中。但这里也存在性能成本,我不完全确定这是否会产生 UB。

我会排除使用 MACROS、C++17 功能(最好仅限 C++11)和非标准功能的解决方案。

谢谢

最佳答案

如果您想维护 MyTrait 类,只需为每个特化定义一个后备模板函数即可。
举个例子:

template<>
struct MyTrait<1> {
    template<typename... Args> static int f(Args&&...) { return {}; }
    static int f(int, int) { return 1; }
};

参见here一个工作示例。

如果您可以重构一下代码,则可以使用自由函数、标记分派(dispatch)和单个回退函数来避免编译错误。
举个例子:

#include<iostream>
#include<utility>

template<int>
struct tag {};

int f(tag<1>, int, int) { return 1; }
int f(tag<2>, int, int, int) { return 100;}
int f(tag<3>, int) { std::cout << "&";  return 120; }

template<typename... Args>
auto caller(int, Args&&... args) -> decltype(f(std::forward<Args>(args)...)) {
    return f(std::forward<Args>(args)...);
}

template<typename... Args>
int caller(char, Args&&... args) {
    return {};
}

template<class... Args>
int switcher(int value, Args&&... arguments) {
    switch(value) {
    case 1:
        caller(0, tag<1>{}, std::forward<Args>(arguments)...);
        break;
    case 2:
        caller(0, tag<2>{}, std::forward<Args>(arguments)...);
        break;
    case 3:
        caller(0, tag<3>{}, std::forward<Args>(arguments)...);
        break;
    default:
        (void)0;
    }

    return 1;
}

int main() {
    switcher(3, 10);
}

编辑

在此评论之后:

I'm trying to remove that switch, and the solution you proposed has a switch

我提出了一个稍微修改过的版本,它不再有开关,而是依赖于递归和继承:

#include <type_traits>
#include<iostream>
#include<utility>

template<int N> struct tag: tag<N-1> {};
template<> struct tag<0> {};

int f(tag<1>, int, int) { return 1; }
int f(tag<2>, int, int, int) { return 100;}
int f(tag<3>, int) { std::cout << "&";  return 120; }

template<typename... Args>
int f(Args&&...) { return {}; }

template<int N, typename... Args>
std::enable_if_t<(N == 0), int>
caller(tag<N> t, Args&&... args) {
    return f(t, std::forward<Args>(args)...);
}

template<int N, typename... Args>
std::enable_if_t<(N > 0), int>
caller(tag<N> t, int value, Args&&... args) {
    return N == value ? f(t, std::forward<Args>(args)...) : caller(tag<N-1>{}, value, std::forward<Args>(args)...);
}

template<class... Args>
int switcher(int value, Args&&... arguments) {
    return caller(tag<3>{}, value, std::forward<Args>(arguments)...);
}

int main() {
    switcher(3, 10);
}

关于c++ - 用于具有不同签名的函数的枚举切换器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43123243/

相关文章:

c++ - 包含 libpqxx 会导致使用 CMake 在 WSL 上构建失败

无需滚动的html模板

C++ 获取 vector 类型

c++ - 触发了一个断点(析构函数),类模板类型是它自己的一个版本?

c++ - 将非托管 C++ 代码转换为托管 C++ 以便从 C# 调用

c++ - Win32 C/C++ 从字节数组中读取 BMP 宽度和高度

c++ - boost::program_options 可以实现这种可重复的选项吗?

sql - 丢弃时间戳中的毫秒部分

java - 如何将通配符 (?) 转换为通用类型 T?

c - 将 float 指针类型转换为 char 指针时会发生什么?