我想在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
实现更喜欢跳转表。无论如何,该接口(interface)不需要重复,只需创建带有两个数组的
constexpr
对象即可。同一类型可以有多个映射。另外,enum
类型是模板化的。而且,可以轻松扩展以支持两个
enum class
而不是仅enum
-int
。我还创建了原始开关实现片段以进行程序集比较:
https://godbolt.org/z/CbEcnZ
PS。我相信可以使用适当的模板推导指南来简化
constexpr Mapping<ExampleEnum, 3> mapping
语法,但我还没有找到如何做的方法。PPS。我将
N
设置为15,循环展开仍在进行:https://godbolt.org/z/-Cpmgm关于c++ - C++ 17中的双向静态值映射,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59147517/