在std :: unordered_set <const char =“”* =“”>中找不到std :: string转换为const char *

时间:2018-03-14 16:03:25

标签: c++ string c++11 visual-studio-2017 unordered-set

在处理项目时,我遇到了以下问题,我无法向自己解释。

我有以下is_in_set(..) function,它只检查cstring是否在cstrings的unordered_set中:

bool is_in_set(const char * str, std::unordered_set<const char *> the_set)
{
    if ( the_set.find( str ) != the_set.end() )
        return true;
    else
        return false;
}

然后我创建了以下示例main方法来演示我的问题:

int main()
{
    std::unordered_set<const char *> the_set({"one",
        "two", "three", "four", "five"});

    std::string str = "three";
    const char * cstr = "three";

    std::cout << "str in set? "
        << is_in_set( str.c_str() , the_set ) << std::endl
        << "cstr in set? " 
        << is_in_set( cstr, the_set ) << std::endl;

    const char * str_conv = str.c_str();

    std::cout << "str_conv in set? "
        << is_in_set( str_conv , the_set ) << std::endl
        << "strcmp(str_conv, cstr) = " << strcmp( str_conv , cstr )
        << std::endl;

    return 0;
}

我期望上面的代码找到std :: string转换为const char *,以及set中的cstring。 而不是,它生成以下输出(Visual Studio Community 2017):

str in set? 0
cstr in set? 1
str_conv in set? 0
strcmp(str_conv, cstr) = 0

我还在两个变量上运行for循环,逐个字节地输出(以十六进制表示),结果如下:

74 68 72 65 65 00 = c_str
74 68 72 65 65 00 = str_conv

为什么在集合中找不到std :: string到const char *? 在这种情况下strcmp不应该返回一个不同于0的值吗?

3 个答案:

答案 0 :(得分:2)

对于const char *==运算符没有重载比较字符串的值,所以我相信unordered_set容器将始终比较指针,而不是指向的值 - 到字符串。

作为优化,编译器可以使用相同的字符使多个字符串文字使用相同的内存位置(因此具有相同的指针),这就是您在使用另一个字符串文字时能够找到该字符串的原因。但是你通过其他机制构造的任何字符串,即使它包含相同的字符,也不会在同一个内存位置,因此指针也不相等。

答案 1 :(得分:1)

如果您确定在使用哈希表时字符串不会离开堆栈,请使用std::unordered_set<std::string>或提供自定义哈希,例如静态变量或用new / malloc等分配。

类似的东西:

struct str_eq {
  bool opeator()(const char* lsh, const char rhs) const noexcept
  {
    return lsh == rhs || 0 == std::strcmp(lsh, rhs);
  }  
};


struct str_hash {
   std::size_t opeator()(const char* str) const noexcept
   {
     // some mur-mur2, google cityhash hash_bytes etc instead of this
      return std::hash<std::string>( std::string(str) ) ();
   }
};

typedef std::unordered_set<const char*, str_hash, str_eq, std::allocator<const char*> > my_string_hashset;

答案 2 :(得分:1)

正如@Daniel Pryden指出的那样,你正在进行地址比较。要解决此问题,您需要拥有unordered_set商店std::string个对象,或者为要使用的unordered_set创建自定义比较。

基于related question的答案,如下所示:

struct StringEqual
{
    bool operator()(const char* a, const char* b) { return 0 == strcmp(a,b); }
};

std::unordered_set<const char *, std::Hash<const char*>, StringEqual> the_set(
    {"one", "two", "three", "four", "five"});

应该做的伎俩。这为unordered_set提供了一个更好的运算符来用于测试字符串。

有关Pred模板参数的详细信息,请参阅documentation

相关问题