首页 > 解决方案 > 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;
    }
};

这有一个明显的缺点,即必须将所有内容编写两次,并且忘记更新其中一个函数会导致不一致。有没有更好的方法?

我需要:

我想拥有:

标签: c++staticmappingc++17

解决方案


我已经尝试过非常天真和简单的实现。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


推荐阅读