一种廉价的方法来创建一个将字符串常量返回枚举的类,反之亦然?

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

做得更好:

class Foo{

std:string option1(MY_ENUM type){
    static const std:string f1 = "a";
    static const std:string f2 = "b";
    static const std:string f3 = "c";
    // ...
    switch (type) {
        case (TYPE_1):{
            return f1;
            break;
        }
        case (TYPE_2):{
            return f2;
            break;
        }
        // ...
    }
}

或者这样做:

class Foo {
private:
    const std:string f1 = "a";
    const std:string f2 = "b";
    const std:string f3 = "c";

public:
std:string option1(MY_ENUM type){

    // ...
    switch (type){
        case (TYPE_1):{
            return f1;
            break;
        }
        case (TYPE_2):{
            return f2;
            break;
        }
        // ...
    }
}

同样,我会做类似的事情将枚举转换为字符串。将字符串保持为函数内的静态const或类中的私有是否更好? - 除了我,没有其他人可以编辑课程。 - 如果类中的任何其他函数知道字符串的值是什么,并且因为它是const,它们无论如何都无法修改它并不重要。

哪个更便宜,并且在运行时和编译期间的开销更少?哪个是更好的做法?

c++ c++11
3个回答
3
投票

更便宜的是根本不使用std::string,因为它可能需要运行时初始化,并且还使用查找表从枚举转换为字符串:

const char *enumToStr(MY_ENUM type) {
    static const char *const names[] = { "a", "b", "c" };
    if (type < sizeof(names) / sizeof(names[0]))
        return names[type];
    return nullptr;
}

不幸的是,你无法使用这种技巧进行反向转换。此外,假设您的枚举数具有连续的数值,并且在添加或重新排序枚举值时应该小心。

至于“把这些值放到哪里”的问题,只要这个数组是静态的或全局的,从“开销”的角度来看并不重要。我建议将它放在定义这些转换函数的* .cpp文件中的匿名命名空间内的全局变量中,这样它就可以用于这两个函数,但在这个转换单元之外是不可见的:

namespace {
    const char *const names[] = ...;
}

3
投票

c ++中最常见的实现是为此设置一个映射:

#include <iostream>
#include <unordered_map>
#include <string>

enum MY_ENUM { 
      TYPE_1
    , TYPE_2 = 42 // Mind the gap please!
};

const char* const option1(MY_ENUM type) {
    static std::unordered_map<MY_ENUM, const char* const> enum2StringMap = 
       { { TYPE_1, "a" }
       , { TYPE_2, "b" }
       // ...
       };

    if(enum2StringMap.find(type) == enum2StringMap.end()) {
        // throw exception or do whatever to indicate an error
        return "Invalid enum value";
    }

    return enum2StringMap[type];
}

反之亦然,您可能需要承担第二张地图的负担,并将其与第一张地图同步:

MY_ENUM option1(const std::string& s) {
    static std::unordered_map<std::string, MY_ENUM> string2EnumgMap = 
       { { "a" , TYPE_1  }
       , { "b" , TYPE_2  }
       // ...
       };

    if(string2EnumgMap.find(s) == string2EnumgMap.end()) {
        // throw exception or do whatever to indicate an error
    }

    return string2EnumgMap[s];
}

使同步更容易的另一个选择(但可能有其他关于性能的缺点)可能是使用qazxsw poi。


boost::bimap

输出:

int main() {
    std::cout << option1(TYPE_1) << std::endl;
    std::cout << option1(TYPE_2) << std::endl;

    std::cout << option1("a") << std::endl;
    std::cout << option1("b") << std::endl;
}

不像a b 0 42 那样“便宜”,但消除了@r3musn0x proposed solution中提到的问题。 在第一次访问时初始化地图时会有一点开销,但任何愚蠢的搜索都可能被优化为O(log n)与O(n)时间复杂度。


看到一个有效的@KonstantinL's comment


1
投票

@ KonstantinL方法的变体;我也认为你可以通过尽可能避免使用online demos来获得最好的服务。但我也想避免地图......

std::string

这使用静态数组而不是地图;连续内存中的线性搜索比使用堆分配节点的树上的二进制搜索更快 - 对于有限数量的值,这似乎就是你的情况。另外 - 这里根本没有使用堆。

笔记:

  • 这个固定大小的阵列支持的“映射”可以在一个单独的头中被分解出来并使其成为通用的。它有时很有用。
  • 如果您的数组已排序,您可以将其中一个搜索二进制。
© www.soinside.com 2019 - 2024. All rights reserved.