【发布时间】:2018-07-25 04:32:52
【问题描述】:
一个简单的问题是:你能在编译时构建类似于switch 的东西吗?答案是肯定的。但是我可以将这样的东西优化(或可能优化)为switch 吗?
解释这个问题:一个例子:
假设我有一个接受整数的函数,我希望它为每个整数输入做一些不同的事情(一种行为)。这很简单,是使用switch 的经典案例。但是假设我有一个实现行为的类型的模板参数包,并且我想为该组行为编写与 switch 等效的内容,并键入它们在包中的索引。当然,这很容易实现,下面有一个示例。
我的实现的问题是,即使在低优化级别,开关也会编译成带有小跳转表的计算跳转。模板化的解决方案变成了一组简单的 if/then/else if/... 子句,并以这种方式编译,给定足够高的优化设置。
例子可以看这里:
在该示例中,您可以在第 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 链)。对于您的情况,索引似乎还可以。