【问题标题】:Mapping c++ string to an enum in order to take user input将 c++ 字符串映射到枚举以获取用户输入
【发布时间】:2014-04-06 20:17:18
【问题描述】:

我正在尝试设计一个类来表示一张卡,我决定使用枚举来表示等级和花色。但是,我需要能够从用户那里获取输入,不幸的是,不可能直接从 cin 获取枚举。

出于这个原因,我打算接收一个字符串并使用std::map 将每个字符串映射到它所代表的枚举值(如this question 中所述)。我也不希望我的输入区分大小写,所以我创建了一个正则表达式,它应该在匹配之前将所有字符转换为小写。

我想出的代码在这里:

istream& operator>>(istream& is, Card& d)
{
std::map<std::string,Card::Rank> mr;
std::map<std::string,Card::Suit> ms;
mr[std::regex("/two/i")] = Card::TWO;
mr[std::regex("/three/i")] = Card::THREE;
mr[std::regex("/two/i")] = Card::FOUR;
mr[std::regex("/two/i")] = Card::FIVE;
mr[std::regex("/two/i")] = Card::SIX;
mr[std::regex("/two/i")] = Card::SEVEN;
mr[std::regex("/two/i")] = Card::EIGHT;
mr[std::regex("/two/i")] = Card::NINE;
mr[std::regex("/two/i")] = Card::TEN;
mr[std::regex("/two/i")] = Card::JACK;
mr[std::regex("/two/i")] = Card::QUEEN;
mr[std::regex("/two/i")] = Card::KING;
ms[std::regex("/clubs/i")] = Card::CLUBS;
ms[std::regex("/diamonds/i")] = Card::DIAMONDS;
ms[std::regex("/hearts/i")] = Card::HEARTS;
ms[std::regex("/spades/i")] = Card::SPADES;

string srank, ssuit;
char c1;

if (is >> srank >> c1 >> ssuit)
{
    if (c1 == 'of')
    {
        Card::Rank rank = mr[srank];
        Card::Suit suit = ms[ssuit];
        d = Card(rank, suit);
    }
    else
    {
       is.clear(ios_base::failbit);
    }
}

return is;
}

但是,在编译时我收到此错误:

error C2679: binary '[' : no operator found which takes a right-hand operand of type 'std::basic_regex<_Elem>' (or there is no acceptable conversion)
      with
      [
          _Elem=char
      ]
      c:\program files (x86)\microsoft visual studio 11.0\vc\include\map(173): could be 'Card::Rank &std::map<_Kty,_Ty>::operator [](std::basic_string<_Elem,_Traits,_Alloc> &&)'
      with
      [
          _Kty=std::string,
          _Ty=Card::Rank,
          _Elem=char,
          _Traits=std::char_traits<char>,
          _Alloc=std::allocator<char>
      ]
      c:\program files (x86)\microsoft visual studio 11.0\vc\include\map(190): or       'Card::Rank &std::map<_Kty,_Ty>::operator [](const std::basic_string<_Elem,_Traits,_Alloc> &)'
      with
      [
          _Kty=std::string,
          _Ty=Card::Rank,
          _Elem=char,
          _Traits=std::char_traits<char>,
          _Alloc=std::allocator<char>
      ]
      while trying to match the argument list '(std::map<_Kty,_Ty>, std::basic_regex<_Elem>)'
      with
      [
          _Kty=std::string,
          _Ty=Card::Rank
      ]
      and
      [
          _Elem=char
      ]

etc.

我以前从未使用过地图,我并没有真正理解我做错了什么;根据我设法挖掘的说明,在我看来这应该可行。

【问题讨论】:

  • char c1; ... if (c1 == 'of') 我希望你看到这段代码有问题。
  • @PaulMcKenzie:好的,所以这可能是糟糕的代码。但是,我认为这不是导致错误的原因......

标签: c++ regex enums operator-overloading


【解决方案1】:

您将mr 声明为从字符串到排名的映射。您正在尝试使用 regex 作为密钥。但是正则表达式不是字符串。选择一个,然后始终如一地使用它。

【讨论】:

  • 那么,我可以将其定义为 std::map<:regex> mr; ?
  • 是的。但是,查找不会按照您想要的方式工作。为什么不直接将输入字符串显式转换为小写?
  • @AlanStokes:这可行,但我希望设置一个正则表达式,因为我将来可能想让输入更宽松。我想我需要以某种方式将 srank 和 ssuit 转换为正则表达式?
【解决方案2】:

根据您所拥有的解决方案:

std::regex 不是std::string,它们是完全不同的类型。我也怀疑,如果你能得到任何合理的结果,使用std::regex 作为映射键。更重要的是,std::map 永远不会像你预期的那样工作。如果您想根据正则表达式列表测试字符串并选择匹配的字符串,您必须这样做:遍历所有正则表达式并测试每个。如果您要放松这些正则表达式,这就是您所需要的。

更简单的方法是将所有读取的字符串转换为小写,然后使用字符串的标准映射。比如:

#include <cctype>

static inline void stringToLower(std::string &s) {
    for (uint i = 0; i < s.size(); ++i) {
        s[i] = tolower(s[i]);
    }
}

istream& operator>>(istream& is, Card& d)
{
    std::map<std::string,Card::Rank> mr;
    std::map<std::string,Card::Suit> ms;
    mr["two"] = Card::TWO;
    mr["three"] = Card::THREE;
    ...
    mr["king"] = Card::KING;

    ms["clubs"] = Card::CLUBS;
    ...

    string srank, ssuit, c1;

    if (is >> srank >> c1 >> ssuit)
    {
        stringToLower(c1);
        if (c1 == "of")
        {
            stringToLower(srank);
            stringToLower(ssuit);
            Card::Rank rank = mr[srank];
            Card::Suit suit = ms[ssuit];
            d = Card(rank, suit);
        }
        else
        {
            is.clear(ios_base::failbit);
        }
    }

    return is;
}

请注意,我将 c1 更改为字符串。在你的版本中,c1 将是 srank 之后的第一个字符(它将是一些空白)。此外,char 值永远不能等于 'of''of' 是 int 类型)。我将其更改为"of"(双引号),其类型为const char *。请记住,如果 c1 是 char*,它将无法正常工作。

顺便说一句,为每个&lt;&lt; 调用分别创建mrms 映射非常低效。考虑使用某种单例。可能的解决方案可能是这样的:

static std::map<std::string,Card::Rank> createmr() {
    std::map<std::string,Card::Rank> mr;
    mr["two"] = Card::TWO;
    mr["three"] = Card::THREE;
    ...
    mr["king"] = Card::KING;
    return mr;
}

static std::map<std::string,Card::Rank> createms() {
    std::map<std::string,Card::Rank> ms;
    ms["clubs"] = Card::CLUBS;
    ...
    return ms;
}

istream& operator>>(istream& is, Card& d)
{
    static std::map<std::string,Card::Rank> mr = createmr(); // note `static` keyword
    static std::map<std::string,Card::Suit> ms = createms();
    ...
}

说明:我在这里使用了static 关键字,在两种不同的地方。一是在functin声明之前。 static 表示此函数在其他源文件中不可用。我还在mrms 声明中使用了它。这意味着,这些变量对于定义它们的所有函数调用都是通用的(在本例中为operator&gt;&gt;)。这样做的一个结果是,createmr() 和 createms() 只会在第一次调用 operator 时被调用一次。

可能更好的方法是在函数外部定义mrms

static std::map<std::string,Card::Rank> mr = createmr(); // note `static` keyword
static std::map<std::string,Card::Suit> ms = createms();

istream& operator>>(istream& is, Card& d)
{
    ...
}

static 关键字在这里意味着,这些变量(现在是全局的)将无法从该源文件外部访问。此处可以省略,但最好将不会在外部使用的所有函数和全局变量都标记为static

【讨论】:

  • 酷,非常感谢!一个问题:为什么单例仍然在创建相同数量的地图时效率更高?
  • 如果你把它设为单调,它会创建 2 个地图一次,而且只有一次。在您的版本中,每次调用&gt;&gt;,它们都是从零开始创建的;另一件事是,最好在函数之外定义它们,并且可能没有static,这样它们也可以被其他函数使用。
  • 谢谢:)。如果我将 createmr() 方法嵌套在我的 >> 覆盖中,我是否会失去性能提升(当它仅在该方法内部使用时,在方法外部声明它似乎很混乱)?
  • 在哪里定义函数不会影响性能。但是你不能在 C++ 中嵌套函数,所以这是不可能的。正如我之前建议的那样,我宁愿考虑在外部定义 mr 和 ms - 您可能在其他功能中也需要它们。请参阅我添加的解释。
  • 非常感谢,这很有道理:)。而且,是的,我忘记了 C++ 不允许嵌套函数!此外,将答案标记为已接受;很抱歉没有早点这样做。
猜你喜欢
  • 2021-11-09
  • 2019-01-29
  • 1970-01-01
  • 1970-01-01
  • 2011-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-06
相关资源
最近更新 更多