为什么以下代码在Visual Studio和GCC上都崩溃了?
要使它崩溃,需要基于范围的for循环,std :: map,std :: string并引用字符串.如果我删除其中任何一个它将工作.
#include <iostream>
#include <string>
#include <map>
using namespace std;
struct S
{
map<string, string> m;
S()
{
m["key"] = "b";
}
const string &func() const
{
return m.find("key")->second;
}
};
int main()
{
for (char c : S().func())
cout << c;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Ideone链接:http://ideone.com/IBmhDH
Yak*_*ont 62
for(:)循环的范围初始化行不会延长除最终临时(如果有)之外的任何内容的生命周期.在for(:)循环执行之前丢弃任何其他临时值.
现在,不要绝望; 这个问题很容易解决.但首先要了解出了什么问题.
代码for(auto x:exp){ /* code */ }扩展为,基本上:
{
auto&& __range=exp;
auto __it=std::begin(__range);
auto __end=std::end(__range);
for(; __it!=__end;++__it){
auto x=*__it;
/* code */
}
}
Run Code Online (Sandbox Code Playgroud)
(在__it和__end行上有一个适度的谎言,所有以变量开头的变量__都没有可见名称.我也在展示C++ 17版本,因为我相信一个更美好的世界,这里的差异并不重要.)
您exp创建一个临时对象,然后返回其中的引用.临时在该行之后死亡,因此您在其余代码中有一个悬空引用.
修复它相对容易.要解决这个问题:
std::string const& func() const& // notice &
{
return m.find("key")->second;
}
std::string func() && // notice &&
{
return std::move(m.find("key")->second);
}
Run Code Online (Sandbox Code Playgroud)
当使用临时值而不是将引用返回到它们时,执行rvalue重载并按值返回移入的值.
那么
auto&& __range=exp;
Run Code Online (Sandbox Code Playgroud)
line确实引用了返回的by-value的生命周期扩展string,并且没有更多的悬空引用.
作为一般规则,永远不要通过引用可能是rvalue的参数来返回范围.
附录:等待,&&和const&之后的方法呢? 右值引用*this?
C++ 11添加了右值引用.但this函数的自我参数是特殊的.要根据被调用对象的rvalue/lvalue-ness选择方法的重载,可以使用&或&&在方法结束后使用.
这与函数的参数类型非常相似. &&在该方法声明该方法应仅在非const rvalues上调用之后; const&意味着应该调用常量左值.不完全匹配的事情遵循通常的预防规则.
如果有一个方法可以将引用返回到对象中,请确保捕获带有&&重载的临时值,并且在这些情况下不返回引用(返回值)或=delete方法.
Sam*_*hik 31
S().func()
Run Code Online (Sandbox Code Playgroud)
这构造了一个临时对象,并调用一个方法,该方法返回std::string对临时对象(位于临时对象std::string的一部分的容器中)所拥有(间接)的引用.
获取引用后,临时对象将被销毁.这也会破坏std::string临时对象(间接)拥有的内容.
在此之后,引用对象的任何进一步使用都将变为未定义的行为.例如迭代其内容.
当涉及到使用范围迭代时,这是一个非常常见的陷阱.你真的也因为被绊倒而感到内疚.
| 归档时间: |
|
| 查看次数: |
3440 次 |
| 最近记录: |