在 std::unordered_set<const char *> 中找不到转换为 const char * 的 std::string
Posted
技术标签:
【中文标题】在 std::unordered_set<const char *> 中找不到转换为 const char * 的 std::string【英文标题】:std::string casted to const char * can not be found in an std::unordered_set<const char *> 【发布时间】:2018-08-23 06:19:38 【问题描述】:在处理一个项目时,我遇到了以下我无法向自己解释的问题。
我有以下is_in_set(..) function,它只是检查一个 cstring 是否在一个 unordered_set 的 cstrings 中:
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;
我希望上面的代码能够找到转换为 const char* 的 std::string 以及集合中的 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
为什么在集合中找不到转换为 const char * 的 std::string? 在这种情况下 strcmp 不应该返回一个不同于 0 的值吗?
【问题讨论】:
如果同一个字符串在不同的地方有两个副本,那么地址显然会不同。您不能通过比较地址来比较 C 字符串。你为什么不做一套std::strings
?如果你真的想让它工作,你必须提供一个自定义比较器。阅读 unordered_set
文档以了解如何操作。
请注意,您能够在您的集合中找到cstr
,因为您的编译器意识到两个字符串文字具有相同的值并将它们合并为一个。此行为不可移植。
std::unordered_set::find
搜索匹配的const char*
值,而不查看它们指向的值。如果您希望进行自定义比较,请考虑在构造时将键相等和散列的自定义谓词传递给std::unordered_set
。
【参考方案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;
【讨论】:
谢谢你的例子,不过我最终只是在任何地方都使用 std::strings !【参考方案2】:正如@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。
【讨论】:
【参考方案3】:对于const char *
,不存在按值比较字符串的==
运算符的重载,因此我相信unordered_set
容器将始终比较指针,而不是指向的字符串的值。
作为一种优化,编译器可以使具有相同字符的多个字符串文字使用相同的内存位置(因此具有相同的指针),这就是为什么当您使用另一个字符串文字时能够找到该字符串的原因。但是你通过其他机制构造的任何字符串,即使它包含相同的字符,也不会位于相同的内存位置,因此指针将不相等。
【讨论】:
太好了,谢谢!出于某种愚蠢的原因,我认为 unordered_set 会提供一个聪明的解决方案来单独比较两者.. :)以上是关于在 std::unordered_set<const char *> 中找不到转换为 const char * 的 std::string的主要内容,如果未能解决你的问题,请参考以下文章
为啥 std::unordered_set 不将 CComBSTR 类型作为键?
对于操作计数()。 std::set<void*> 或 std::unordered_set<void*> 哪个更快?
std :: unordered_set :: find - 仅为find()构造一个实例