使用std::string_view作为std::unordered_map的键时,是否需要保证底层字符串/字符的存活?——键被异常覆盖问题求助
嘿,我帮你揪出这个问题的病根了,咱们一步步来拆解:
问题到底出在哪?
你的核心问题是踩了**std::string_view的生命周期陷阱**。咱们捋一遍代码的执行过程就懂了:
- 调用
wrapper("test1")时,函数里的std::string str是个栈上的局部变量,它会临时持有"test1"的内容。 - 你把这个
str转成std::string_view传给emplace_in_map——记住,string_view本质就是个“指向字符串的指针+长度”,它不复制内容,只是个视图。 wrapper执行完,栈上的str被销毁,这块内存就被释放了(或者说,后续调用函数时很可能会被复用)。- 当你调用
wrapper("test2")时,新的std::string str刚好占用了之前那块栈内存,内容变成了"test2"。 - 这时候你去看map里的两个键,第一个
string_view其实已经悬空了,它指向的内存现在存的是"test2"——并不是第一个键被覆盖,而是两个string_view都指向了同一块被复用的栈内存,所以看起来两个键都是"test2"。
更要命的是,这种情况属于C++里的未定义行为,后续任何对这两个string_view的操作(比如哈希计算、键比较)都可能搞出各种奇怪的bug。
怎么修复?
根据你的需求,有两种靠谱的修复方式:
方案1:把键换成std::string(最稳妥)
std::string会持有字符串的副本,完全不用担心生命周期问题,这也是最推荐的做法:
std::unordered_map<std::string, std::unique_ptr<CustomClass>> m_map; void emplace_in_map(std::string str, std::unique_ptr<CustomClass> ccc){ m_map.emplace(std::move(str), std::move(ccc)); } void wrapper(std::string str) { auto ccc = std::make_unique<CustomClass>(); // 这里替换成你实际创建对象的逻辑 emplace_in_map(std::move(str), std::move(ccc)); }
这样每个键都会独立持有自己的字符串副本,生命周期和map绑定,再也不会出现悬空的问题。
方案2:确保string_view指向的内存长期有效
如果你非要用string_view当键,必须保证它指向的字符串内存,在map存在的整个期间都不会被销毁。比如:
- 直接传字符串字面量(比如
"test1",它们的内存是全局的,整个程序运行期间都存在); - 或者把字符串存储在其他长期存活的容器里(比如全局的
std::vector<std::string>),再用string_view指向它们。
修改后的wrapper可以直接接收string_view:
void wrapper(std::string_view str) { // 直接用string_view接收字面量 auto ccc = std::make_unique<CustomClass>(); emplace_in_map(str, std::move(ccc)); } int main(){ wrapper("test1"); // 字面量内存全局有效,不会销毁 wrapper("test2"); // 现在两个键是独立的,再也不会“被覆盖”了 }
但要注意:如果传入的是临时std::string的string_view,还是会回到之前的坑,所以必须严格管控string_view指向的内存生命周期。
总结一下
std::string_view是个非拥有型的视图,它不负责管理指向的内存,所以用它当容器键的时候,一定要确保底层内存的生命周期比容器长。如果做不到这一点,就老老实实用std::string当键,别给自己找麻烦~
内容的提问来源于stack exchange,提问作者ATK
相关产品推荐
相关产品推荐

