c ++ 17中的双向静态值映射

问题描述 投票:0回答:1

我想在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++ static mapping c++17
1个回答
0
投票

避免使用此类代码会更好。它们倾向于违反软件开发的基本原理之一The Open-Closed Principle

您可以通过将MyMapping设置得一般些来改进。让更高级别的类/函数定义映射。

class MyMapping {
   public:
      void registerItem(ExampleEnum eValue, int intValue)
      {
         enumToIntMap[eValue] = intValue;
         intToEnumMap[intValue] = eValue;
      }

      std::optional<int> enumToInt(ExampleEnum v) {
         auto iter = enumToIntMap.find(v);
         if ( iter != enumToIntMap.end() )
         {
            return iter->second;
         }
         else
         {
            return std::nullopt;
         }
      }

      std::optional<ExampleEnum> intToEnum(int v) {
         auto iter = intToEnumMap.find(v);
         if ( iter != intToEnumMap.end() )
         {
            return iter->second;
         }
         else
         {
            return std::nullopt;
         }
      }

      std::map<ExampleEnum, int> enumToIntMap;
      std::map<int, ExampleEnum> intToEnumMap;
};

更高级别的功能可以是:

void initMyMapping(MyMapping& mapping)
{
   mapping.registerItem(A, 1);
   mapping.registerItem(B, 2);
   mapping.registerItem(D, 42);
}

我知道这仍然违反了开闭原则,但程度较轻。如果要为CE添加映射数据,则必须为此添加代码。但是,您可以执行此操作而无需更改MyMapping。您还可以选择在第二个功能中执行此操作,而不更改initMyMapping

void initMyMapping_extend(MyMapping& mapping)
{
   mapping.registerItem(C, 22);
   mapping.registerItem(E, 38);
}
© www.soinside.com 2019 - 2024. All rights reserved.