c++ - c++17中的双向静态值映射
问题描述
我想在 C++17 中有效地双向映射一些不同类型的值(只有很少值的 1:1 映射)。考虑例如映射枚举值和整数,尽管该问题也适用于其他类型。目前,我正在这样做:
#include <optional>
enum class ExampleEnum { A, B, C, D, E };
class MyMapping {
public:
std::optional<int> enumToInt(ExampleEnum v) {
switch(v) {
case ExampleEnum::A:
return 1;
case ExampleEnum::B:
return 5;
case ExampleEnum::D:
return 42;
}
return std::nullopt;
}
std::optional<ExampleEnum> intToEnum(int v) {
switch(v) {
case 1:
return ExampleEnum::A;
case 5:
return ExampleEnum::B;
case 42:
return ExampleEnum::D;
}
return std::nullopt;
}
};
这有一个明显的缺点,即必须将所有内容编写两次,并且忘记更新其中一个函数会导致不一致。有没有更好的方法?
我需要:
- 一致性。在映射和反向映射中不可能有不同的语义。
- 编译时定义。映射的值是预先知道的,并且在运行时不会改变。
- 运行时查找。在编译时不知道将查找哪些值,甚至可能根本不包含映射(返回一个空的可选值)。
我想拥有:
- 没有额外的内存分配
- 与双开关方式的性能基本相同
- 使映射定义易于扩展的实现(即在将来添加更多值或将其应用于其他类型)
解决方案
我已经尝试过非常天真和简单的实现。https://godbolt.org/z/MtcHw8
#include <optional>
enum class ExampleEnum { A, B, C, D, E };
template<typename Enum, int N>
struct Mapping
{
Enum keys[N];
int values[N];
constexpr std::optional<Enum> at(int x) const noexcept
{
for(int i = 0; i < N; i++)
if(values[i] == x) return keys[i];
return std::nullopt;
}
constexpr std::optional<int> at(Enum x) const noexcept
{
for(int i = 0; i < N; i++)
if(keys[i] == x) return values[i];
return std::nullopt;
}
};
constexpr Mapping<ExampleEnum, 3> mapping{{ExampleEnum::A, ExampleEnum::B, ExampleEnum::D},
{111, 222, 333}};
int main()
{
int x = rand(); // Force runtime implementation
auto optEnum = mapping.at(x);
if(optEnum.has_value())
return *mapping.at(ExampleEnum::B); // Returns 222, (asm line 3) constexpr works
auto y = (ExampleEnum)rand(); // Force runtime implementation
auto optInt = mapping.at(y);
if(optInt.has_value())
return (int)*mapping.at(333); // Returns 3, constexpr works
return 0;
}
它利用循环展开来实现int -> ExampleEnum
映射中的切换方法性能。
映射的组装ExampleEnum -> int
非常模糊,因为优化器利用了枚举值是有序的这一事实,并且更喜欢跳转表而不是if-else
实现。
无论如何,接口不需要重复,只需创建constexpr
带有两个数组的对象即可。您可以有多个相同类型的映射。此外,enum
类型是模板化的。
此外,它可以轻松扩展以支持两个enum class
而不是仅enum
- int
。
我还创建了带有原始开关实现的片段以进行组装比较: https ://godbolt.org/z/CbEcnZ
PS。我相信constexpr Mapping<ExampleEnum, 3> mapping
可以通过适当的模板推导指南简化语法,但我还没有找到如何去做。
聚苯乙烯。我N
最多选择了 15 个,循环展开仍在进行中:https ://godbolt.org/z/-Cpmgm
推荐阅读
- c# - TcpClient 505 HTTP 版本不支持
- flutter - 如何在我的 bloc 和 rxdart 上添加日期选择器
- php - 当它进入服务器codeigniter时如何解决错误请求?
- javascript - 如何根据另一个数组对数组进行排序?(Javascript)
- hash - Raku:访问多维哈希值的问题
- c# - 一个地方如何围绕给定轴指向?
- python - 如何使用 max_feature = n 在 TfidfVectorizer 中对顶级特征进行排序,其中 n 不是无?
- smtp - 突然在 phpmailer 上遇到 SMTP ERROR
- python - Django - 从给定日期之前或之后创建的表中获取下一个 X 实体?
- sql-server - 'Case' 附近的语法不正确。需要 ID、QUOTED_ID 或“。” SQL Server 中的错误