std::unordered_map 不断导致错误,这是一个错误吗?

Posted

技术标签:

【中文标题】std::unordered_map 不断导致错误,这是一个错误吗?【英文标题】:std::unordered_map keeps resulting in error, is this a bug? 【发布时间】:2016-03-14 01:41:18 【问题描述】:

所以我有这个函数用于散列内部字符串,但是当我尝试运行它时,Visual Studio 2015 给了我一个调试断言失败!错误:

Program: C:\WINDOWS\SYSTEM32\MSVCP140D.dll
File: c:\program files (x86)\microsoft visual studio 14.0\vc\include\vector
Line: 1232

Expression: vector subscript out of range

现在第一次调用 InternalString 时,我收到此错误,它在 gStringIdTable.find(sid) 行上中断。

static std::unordered_map<StringId, const char*> gStringIdTable;

StringId InternalString(const char* string) 
    StringId sid = std::hash<std::string>()(string);

    std::unordered_map<StringId, const char*>::iterator it = gStringIdTable.find(sid);

    if (it == gStringIdTable.end()) 
        gStringIdTable.insert(sid, string);
    

    return sid;

我认为这可能是我初始化迭代器的方式有问题,所以我想试试这个:

if (gStringIdTable.find(sid) == gStringIdTable.end()) 
    gStringIdTable.insert(sid, string);

但这给了我同样的错误。然后我想这可能与在 unordered_map 填充任何内容之前进行查找有关,所以我尝试只在函数中插入。但这也给了我同样的错误。我尝试将 const char* 转换为 std::string,然后仅在this answer's 建议处处理 unordered_map 中的字符串,但得到了同样的错误。我尝试使用 emplace 代替 insert,尝试使用 std::make_pair,但所有组合均无济于事。

现在,我是否遗漏了一些明显错误的东西,或者某处有错误?

更新

好的,这是一个编译版本,但我仍然得到错误。我在 Visual Studio 2015 中启动了一个空的 c++ 项目,并添加了这 3 个文件以匹配它当前在我的项目中的实现方式:

main.cc

#include "stringid.h"

const static mynamespace::StringId kSidOne = mynamespace::InternalString("One");

int main(int argc, char *argv[]) 
    return 0;

stringid.cc

#include "stringid.h"
#include <string>
#include <unordered_map>

namespace mynamespace 

static std::unordered_map<StringId, std::string*> gStringIdTable;

StringId InternalString(const char* string) 
    StringId sid = std::hash<std::string>()(string);

    if (gStringIdTable.find(sid) == gStringIdTable.end()) 
        gStringIdTable.emplace(sid, new std::string(string));
    

    return sid;


 // mynamespace

字符串.h

#ifndef STRINGID_H_
#define STRINGID_H_

namespace mynamespace 

typedef unsigned int StringId;
StringId InternalString(const char* string);

 // mynamespace

#endif // STRINGID_H_

我还对函数进行了一些调试,看看我是否能找出问题出在哪里,看起来当 find 函数抓取相关存储桶时,它返回 null 或 0,然后 _Begin 函数抛出错误,因为大小等于零。

小更新

我也试过用 gcc 编译。它编译得很好,但我仍然在 find() 上遇到错误。

【问题讨论】:

我试图重现该问题但失败了。它对我来说运行得很好。请提供一个mcve 而不是几个sn-ps。 听起来像静态初始化命令惨败。特别是,gStringIdTable 不能保证在调用 InternalString 以初始化 kSidOne 时被正确初始化。 @T.C.哦耶!!我什至不认为这可能是一个问题!这是我的第一个严肃的 C++ 项目,所以我相信我将来还会遇到更多这样的问题。谢谢你的帮助!! 【参考方案1】:

您正在使用...散列作为哈希表的键。

这是一个错误。哈希不是唯一的。

您要做的是用...键来对哈希表进行键控!

表如何散列是一个实现细节,你不应该在外面看到。

解决此问题的最简单方法是,例如使用std::unordered_set&lt;std::string&gt;

Live On Coliru

#include <unordered_set>

const char* InternalString(const char* string) 
    static std::unordered_set<std::string> s_table;
    std::unordered_set<std::string>::iterator it = s_table.find(string);

    return (it != s_table.end())? it->c_str() : s_table.insert(string).first->c_str();


#include <cassert>

int main() 
    auto a = InternalString("HelloWorld" + 5);
    auto b = InternalString("World");

    assert(a == b);

断言验证正常,因为 WorldWorld 匹配,即使原始指针不同。

您可以提高效率(例如,使用一些带有自定义键比较器的集合)

【讨论】:

哈希只返回一个我在其他代码中使用的整数,因此我不必比较字符串,只需比较整数即可。我不能使用哈希函数产生的整数作为键? 除非您不关心唯一键或字符串与键的一对一映射,否则不会。 添加了一些有用的(希望如此)示例。如果您愿意,可以与这个较旧的答案进行比较:***.com/a/20961180/85371 是的,这行得通,但我仍然不明白为什么我的实现如此糟糕。 如果您发布一个显示问题的最小自包含可编译示例,我将向您展示问题。

以上是关于std::unordered_map 不断导致错误,这是一个错误吗?的主要内容,如果未能解决你的问题,请参考以下文章

带有 std unordered_map 的 SIGFPE

CDT 索引器找不到 std::unordered_map

std :: unordered_map向量下标超出范围

C ++:访问unordered_map中的数据时出现段错误以设置

解决方法 GCC 5.5 unordered_map 错误

std::unordered_map::clear() 做啥?