C++ 为什么在基于范围的初始值设定项中使用临时对象会导致崩溃?
为什么以下代码在VisualStudio和GCC上都会崩溃 要使其崩溃,它需要基于范围的For循环、std::map、std::string和对字符串的引用。如果我移除其中任何一个,它都会起作用C++ 为什么在基于范围的初始值设定项中使用临时对象会导致崩溃?,c++,c++11,C++,C++11,为什么以下代码在VisualStudio和GCC上都会崩溃 要使其崩溃,它需要基于范围的For循环、std::map、std::string和对字符串的引用。如果我移除其中任何一个,它都会起作用 #include <iostream> #include <string> #include <map> using namespace std; struct S { map<string, string> m; S() {
#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;
}
#包括
#包括
#包括
使用名称空间std;
结构
{
地图m;
S()
{
m[“键”]=“b”;
}
常量字符串&func()常量
{
返回m.find(“key”)->second;
}
};
int main()
{
对于(char c:S().func())
库特
这将构造一个临时对象,并调用一个方法,该方法返回对临时对象(间接)拥有的std::string
的引用(该std::string
位于作为临时对象一部分的容器中)
获取引用后,临时对象将被销毁。这也会销毁临时对象(间接)拥有的std::string
在此之后,引用对象的任何进一步使用都将成为未定义的行为,例如对其内容进行迭代
这是一个非常常见的陷阱,当涉及到使用范围迭代时。你真的很抱歉被它绊倒了
这将构造一个临时对象,并调用一个方法,该方法返回对临时对象(间接)拥有的std::string
的引用(该std::string
位于作为临时对象一部分的容器中)
获取引用后,临时对象将被销毁。这也会销毁临时对象(间接)拥有的std::string
在此之后,引用对象的任何进一步使用都将成为未定义的行为,例如对其内容进行迭代
在使用范围迭代时,这是一个非常常见的陷阱。您的用户也会因此而感到内疚。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 */
}
}
(在\uuu it
和\uu end
行上有一个适度的谎言,所有以\uuuu
开头的变量都没有可见的名称。此外,我展示的是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);
}
在使用临时表时,不要执行右值重载并按值返回移动到的值,而不要将引用返回到临时表中
然后
auto&& __range=exp;
行引用了返回的by值字符串的生存期扩展,不再有悬空引用
一般来说,永远不要通过引用可能是右值的参数来返回范围
附录:等待,&
和常量&
在方法之后
C++11添加了右值引用。但是函数的this
或self参数是特殊的。要根据被调用对象的右值/左值选择方法的重载,可以在方法结束后使用&
或&
这与函数的参数类型非常相似。&
在该方法声明只应在非常量值上调用该方法之后;常量&
表示应为常量左值调用该方法。不完全匹配的情况遵循通常的精度规则
当您有一个将引用返回到对象中的方法时,请确保捕获带有&
重载的临时对象,并且在这些情况下不返回引用(返回值),或者=删除该方法。针对(:)的的范围初始化行
循环只会延长最终临时变量(如果有)的生存期。在(:)的循环执行之前,会丢弃任何其他临时变量
现在,不要绝望;这个问题有一个简单的解决方法。但首先,我们来看看哪里出了问题
(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 */
}
}
(在\uuu it
和\uu end
行上有一个适度的谎言,所有以\uuuu
开头的变量都没有可见的名称。此外,我展示的是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);
}
在使用临时表时,不要执行右值重载并按值返回移动到的值,而不要将引用返回到临时表中
然后
auto&& __range=exp;
行引用了返回的by值字符串的生存期扩展,不再有悬空引用
一般来说,永远不要通过引用可能是右值的参数来返回范围
附录:等待,&
和常量&
在方法之后
C++11添加了右值引用。但是函数的this
或self参数是特殊的。要根据被调用对象的右值/左值选择方法的重载,可以在方法结束后使用&
或&
这与函数的参数类型非常相似。&&
在方法声明该方法应