C++中右值unordered_map在范围for循环中的未定义行为
问题分析:临时对象生命周期导致的悬垂引用问题
这问题确实挺容易让人摸不着头脑的,核心原因是临时对象的生命周期限制和悬垂引用在作祟,咱们一步步拆解清楚:
第一个循环的问题所在
先看你第一段代码:
for (const int i : std::unordered_map<int, std::vector<int>> {{0, std::vector<int> {1}}}.at(0)) { std::cout << i << std::endl; //prints 0 }
这里你直接创建了一个临时的unordered_map对象,紧接着调用at(0)获取内部vector的引用。但C++里临时对象的生命周期只覆盖到它所在的完整表达式结束——这里的完整表达式就是std::unordered_map<...>.at(0)这一串代码。
当这个表达式执行完毕,临时的map就会被立即销毁,它内部存储的vector也会跟着被析构。这时候你的范围for循环拿到的是一个指向已经被销毁的vector的悬垂引用,访问这个引用属于C++标准里明确的未定义行为。你看到输出0完全是随机的巧合,换个编译器、编译选项甚至运行环境,结果都可能不一样(比如输出乱码、程序崩溃)。
第二个循环为什么正常
再看第二段代码:
std::unordered_map<int, std::vector<int>> map { {0, std::vector<int> {1}} }; for (const int i : map.at(0)) { std::cout << i << std::endl; //prints 1 }
这里的map是一个有名字的局部变量,它的生命周期会持续到main函数执行完毕。所以map.at(0)返回的引用始终指向一个有效的、存活的vector,范围for循环自然能正确遍历并输出1。
怎么修复第一个写法
如果想让第一种写法也能正常工作,你可以把临时map绑定到一个const引用上——C++标准规定,临时对象绑定到const引用时,它的生命周期会延长到这个引用的生命周期结束:
const auto& temp_map = std::unordered_map<int, std::vector<int>> {{0, std::vector<int> {1}}}; for (const int i : temp_map.at(0)) { std::cout << i << std::endl; // 现在会正确输出1 }
内容的提问来源于stack exchange,提问作者Dominic Abbott
相关产品推荐
相关产品推荐

