【问题标题】:Building a switch at compile time在编译时构建开关
【发布时间】:2018-07-25 04:32:52
【问题描述】:

一个简单的问题是:你能在编译时构建类似于switch 的东西吗?答案是肯定的。但是我可以将这样的东西优化(或可能优化)为switch 吗?

解释这个问题:一个例子:

假设我有一个接受整数的函数,我希望它为每个整数输入做一些不同的事情(一种行为)。这很简单,是使用switch 的经典案例。但是假设我有一个实现行为的类型的模板参数包,并且我想为该组行为编写与 switch 等效的内容,并键入它们在包中的索引。当然,这很容易实现,下面有一个示例。

我的实现的问题是,即使在低优化级别,开关也会编译成带有小跳转表的计算跳转。模板化的解决方案变成了一组简单的 if/then/else if/... 子句,并以这种方式编译,给定足够高的优化设置。

例子可以看这里:

https://godbolt.org/g/fxQF1U

在该示例中,您可以在第 690 行的程序集中看到基于 switch 的实现。模板版本可以在第 804 行找到。我还在此处包含示例的源代码,以防链接失效。

我知道编译器可以根据“as if”子句进行尽可能多的优化,但是有没有办法这样编写,以便编译器更有可能生成更优化的代码?

#include <tuple>
#include <type_traits>
#include <any>
#include <string>
#include <vector>
#include <typeinfo>
#include <iostream>
#include <random>

template <typename... T>
struct Types {};

template <int I>
std::any get_type_name2p(int n) {
  return unsigned();
}

template <int I, typename T, typename... Rest>
std::any get_type_name2p(int n) {
  if (n == I) {
    return T();
  }
  return get_type_name2p<I + 1, Rest...>(n);
}

std::any get_type_name2(int n)
{
  return get_type_name2p<
    0, int, float, std::string, std::vector<int>,
    std::vector<float>, std::vector<double>, double, char>(n);
}

std::any get_type_name(int n)
{
  switch (n) {
   case 0:
    return int();
   case 1:
    return float();
   case 2:
    return std::string();
   case 3:
    return std::vector<int>();
   case 4:
    return std::vector<float>();
   case 5:
    return std::vector<double>();
   case 6:
    return double();
   case 7:
    return char();
   default:
    return unsigned();
  }
}

int main()
{
  std::random_device rd;
  std::mt19937 mt(rd());
  std::uniform_int_distribution<int> dist(1, 10);

  auto n = dist(mt);
  auto x = get_type_name(n);
  std::cout << x.type().name() << std::endl;

  auto y = get_type_name2(n);
  std::cout << y.type().name() << std::endl;

  return 0;
}

【问题讨论】:

  • 这个问题可以更准确地描述为“类型的开关”。
  • 尝试用产生any的lambda工厂函数填充一个数组。由于它们不是捕获的,它们隐式转换为函数指针,不需要std::function。然后只需索引该数组。
  • 您可能想要std::variant 而不是手动执行此操作。
  • switch 转换为代码可能会使用多种策略,因此通常,您必须选择模板等效的策略(索引、映射、if 链)。对于您的情况,索引似乎还可以。

标签: c++ c++17


【解决方案1】:

这是一个可能的解决方案:

std::any get_type_name(int n)
{

  if ((n<0) || (n>7)) { n = 8; }
  std::any types[9]=
  {
    int(),
    float(),
    std::string(),
    std::vector<int>(),
    std::vector<float>(),
    std::vector<double>(),
    double(),
    char(),
    unsigned()
  };

  return types[n];
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-24
    • 2020-08-22
    • 2014-01-06
    • 2011-10-19
    • 2017-07-03
    • 2017-03-18
    相关资源
    最近更新 更多