C++ 静态常量std::map<;字符串,int>;如果埃尔塞夫

C++ 静态常量std::map<;字符串,int>;如果埃尔塞夫,c++,c++11,if-statement,dictionary,C++,C++11,If Statement,Dictionary,我写了一个函数,可以将字符串转换成数字。我看到了两种可能的变体: int convert(const std::string input) { if (input == "one") { return 1; } else if (input == "two") { return 2; } // etc. return 0; } 或 int转换(常量std::字符串输入){ 静态常量映射表={ {“一”,1}, {“两个”,

我写了一个函数,可以将字符串转换成数字。我看到了两种可能的变体:

int convert(const std::string input) {
    if (input == "one") {
        return 1;
    } else if (input == "two") {
        return 2;
    }
    // etc.
    return 0;
}

int转换(常量std::字符串输入){
静态常量映射表={
{“一”,1},
{“两个”,2}
//等等。
}
const auto result=table.find(输入);
if(result==table.end())
{
返回0;
}
返回结果->秒;
}

哪种方法更有效/可接受/可读?

对于少量可能的输入值,我更希望解决方案1简单明了并且可能具有最佳性能

如果值列表变得太大,那么您真正需要的是整数和写入的数字之间的转换,这真的是另一回事(请参阅NathanOliver评论中引用的“人性化者”库,如果其他

An
If else
(或
开关
,如果您可用)适用于小案例,您还可以控制测试的顺序,以防最常见的测试可以快速停止搜索,您可以先测试它们

在许多情况下,
开关
远远优于
if else
s列表。两者都更容易阅读,而且可能更快。尽管
开关
不是
字符串
的最佳选择

但是,您可以打开
枚举
,而不是使用字符串;这当然是更好的方法,除了
映射


map
std::unordered\u map
对于大量可能性,或者当您需要在运行时更新这些可能性时,效果要好得多。

对于少量文本,我会使用一个简单的查找表:

struct LookupTable {
    const char* text;
    int value;
};
const LookupTable table[] = {
    { "one", 1 },
    { "two", 2 }
};
int convert(const char* text) {
    if (!text) return 0;
    for (int i=0; i<sizeof(table)/sizeof(LookupTable); i++) {
        if (strcasecmp(text, table[i].text) == 0 ) {
            return table[i].value;
        }
    }
    return 0;
}

答案在很大程度上取决于您将支持多少不同的字符串

一些字符串:使用if-else。以后理解代码所需的努力很少

大量字符串:创建映射。与读取大量if-else构造相比,理解代码的工作量很小。您可能需要经常扩展此列表。添加数据需要更少的键入


我不确定C++的映射使用字符串作为键有多聪明。在最坏的情况下,两者都具有相同的性能。如果列表非常大,您可能会考虑创建字符串的哈希值并将其用作键。这可能会大大提高性能。但您必须确保不会发生冲突。(一个好的散列算法和64位散列大小就足够了。)可能是现代map实现已经做到了这一点。

我建议
map
。主要原因是它在这个词的两种可能含义上都具有更好的伸缩性

如果将来可能需要添加更多条件,则使用映射更易于维护和管理。此外,它允许运行时修改查找表,这在某些上下文中非常有用

我在开发中遇到了一个类似的问题,像这样的查找必须由子类修改。我认为映射提供了更大的灵活性。映射允许我定义一个虚拟函数,如
getLookup()
,它返回一个查找表。在该函数中,我可以保留一个静态映射(我按照第一次调用时需要的方式设置)特定于该类类型。如果您正在考虑此类应用程序,那么我强烈建议映射If链。If链在继承中完全不可管理。您将开始问“如何更改X解析为什么?”迟早会有,除了意大利面之外,没有什么切实可行的答案


另一个评论:考虑。对于这个用例,范围迭代似乎不太可能。

< P>如果在地图搜索O(log n)的情况下,如果否则搜索具有O(n)的复杂性。 而且,当列表变长时,if-else语句将变得不可读。因此,map更好

另一方面,关于函数声明中的参数:

int convert(const std::string input)
我会将其更改为通过常量引用传递,而不是通过常量副本传递,以提高效率:

int convert(const std::string& input)
这是其中一个非常适合:

这类似于@Calvin的查找表方法,而不必在多个位置跟踪多组数据

//alphabetically sorted by string X macro

#define MAP_AS_ENUM(e,v,s) MYENUM_##e,
#define MAP_AS_STRING(e,v,s) s,
#define MAP_AS_VALUE(e,v,s) v,
#define MYMAP(OP) \
  OP(NONE,  -1,"") \
  OP(FIVE,  5, "five") \
  OP(FOUR,  4, "four") \
  OP(ONE,   1, "one") \
  OP(THREE, 3, "three") \
  OP(TWO,   2, "two") \
  OP(ZERO,  0, "zero")

enum myenums{ MYMAP(MAP_AS_ENUM) };
char *mystrings[] = { MYMAP(MAP_AS_STRING) };
char myvalues[]={ MYMAP(MAP_AS_VALUE) };

//now you can use a binary search on mystrings to get the index
//which will correspond to the associated enum
哪种方式更有效/可接受/可读

如果只有两个值,
if
/
else
解决方案是最有效的,而且肯定非常简单,特别是对于不习惯标准库的人来说,但是它很快就会陷入混乱

因此,当你到达一个5个或更多的项目,切换到使用容器

警告:不幸的是,避免内存分配的
std::string_视图
仍然不是标准的;为了简单起见,我将使用
std::string
,但如果内存分配是一个问题,
std::string_视图
或自定义
CStr
类会更好

有3种有效的选择:

  • std::map
    std::unordered\u map
    是最直观的选择,目前还不清楚哪一种更快
  • std::vector
    (排序)总是比
    std::map
因此,如果效率是一个问题:

int convert(std::string const& name) {
    static std::vector<std::pair<std::string, int>> const Table = []() {
        std::vector<std::pair<std::string, int>> result = {
            { "one", 1 },
            { "two", 2 },
            { "three", 3 },
            { "four", 4 }
        };
        std::sort(result.begin(), result.end());
        return result;
    }();

    auto const it =
        std::lower_bound(Table.begin(), Table.end(), std::make_pair(name, 0));

    if (it != Table.end() and it->first == name) {
        return it->second;
    }
    return 0;
}
int转换(std::string const&name){
静态std::vector const Table=[](){
标准::向量结果={
{“一”,1},
{“两个”,2},
{“三”,3},
{“四”,4}
};
排序(result.begin(),result.end());
返回结果;
}();
自动const it=
std::下限(Table.begin()、Table.end()、std::make_pair(name,0));
if(it!=Table.end()和it->first==name){
返回->秒;
}
返回0;
}
毕竟,排序数组是执行二进制搜索的最有效方式,因为它具有更好的缓存行为。
//alphabetically sorted by string X macro

#define MAP_AS_ENUM(e,v,s) MYENUM_##e,
#define MAP_AS_STRING(e,v,s) s,
#define MAP_AS_VALUE(e,v,s) v,
#define MYMAP(OP) \
  OP(NONE,  -1,"") \
  OP(FIVE,  5, "five") \
  OP(FOUR,  4, "four") \
  OP(ONE,   1, "one") \
  OP(THREE, 3, "three") \
  OP(TWO,   2, "two") \
  OP(ZERO,  0, "zero")

enum myenums{ MYMAP(MAP_AS_ENUM) };
char *mystrings[] = { MYMAP(MAP_AS_STRING) };
char myvalues[]={ MYMAP(MAP_AS_VALUE) };

//now you can use a binary search on mystrings to get the index
//which will correspond to the associated enum
int convert(std::string const& name) {
    static std::vector<std::pair<std::string, int>> const Table = []() {
        std::vector<std::pair<std::string, int>> result = {
            { "one", 1 },
            { "two", 2 },
            { "three", 3 },
            { "four", 4 }
        };
        std::sort(result.begin(), result.end());
        return result;
    }();

    auto const it =
        std::lower_bound(Table.begin(), Table.end(), std::make_pair(name, 0));

    if (it != Table.end() and it->first == name) {
        return it->second;
    }
    return 0;
}
int convert(const std::string& input) {
    static const std::array<std::string, 9> numbers 
        = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
    auto find_result = std::find(numbers.begin(), numbers.end(), input);
    if (find_result == numbers.end())
        return 0;
    return std::distance(numbers.begin(), find_result) + 1;
}