为什么在基于范围的初始化程序中使用临时对象会导致崩溃? [英] Why does using a temporary object in the range-based for initializer result in a crash?

查看:96
本文介绍了为什么在基于范围的初始化程序中使用临时对象会导致崩溃?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么下面的代码在Visual Studio和GCC上都崩溃?



要使其崩溃,它需要基于范围的for循环,std :: map,std :: string并引用该字符串。如果我删除其中任何一个,它将起作用。

  #include< iostream> 
#include< string>
#include< map>
使用命名空间std;

结构S
{
map< string,string> m;

S()
{
m [ key] = b;
}

const string& func()const
{
return m.find( key)-> second;
}
};

int main()
{
for(字符c:S()。func())
cout<< C;

返回0;
}

Ideone链接: http://ideone.com/IBmhDH

解决方案

范围初始化行 for(:)循环的使用不会延长除 final 临时(如果有)之外的任何内容的生命周期。在 for(:)循环执行之前,所有其他临时对象都将被丢弃。



现在,不要灰心;这个问题很容易解决。但是首先要弄清楚出了什么问题。



代码 for(auto x:exp){/ *代码* /} 基本上扩展为:

  {
auto&& __range = exp;
auto __it = std :: begin(__ range);
auto __end = std :: end(__ range);
for(; __it!= __ end; ++ __ it){
auto x = * __ it;
/ *代码* /
}
}

__ it __ end 行以及所有以 __开头的变量没有可见的名称。我还展示了C ++ 17版本,因为我相信世界会更好,这里的区别也无所谓。)



您的 exp 创建一个临时对象,然后在其中返回对它的引用。临时模板在该行之后消失,因此其余代码中都有悬挂的引用。



修复此问题相对容易。要解决此问题:

  std :: string const& func()const& //注意& 
{
return m.find( key)-> second;
}
std :: string func()&& //注意&&
{
return std :: move(m.find( key)-> second);;
}

执行右重载并按值返回移入的值

然后使用

 自动&& __range = exp; 

行确实引用了返回的按值字符串,不再有悬空的引用。



通常,切勿通过引用可能是右值的参数来返回范围。






附录:等待,&& const& 之后的方法? 正确引用 * this



C ++ 11添加了右值引用。但是函数的 this 或self参数是特殊的。要根据所调用对象的右值/左值性选择方法的重载,可以使用& && 在方法结束之后。



这与函数的参数类型非常相似。方法后的&&指出应仅在非常量值上调用该方法; const& 表示应为常量左值调用它。不完全匹配的事物遵循通常的优先规则。



当您有一种将引用返回到对象的方法时,请确保使用<$捕获临时对象c $ c>& 重载,或者在这种情况下不返回引用(返回值),或者 = delete 方法。


Why does the following code crash both on Visual Studio and GCC?

For it to crash it requires the range-based for loop, std::map, std::string and taking a reference to the string. If I remove any one of them it will work.

#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;
}

Ideone link: http://ideone.com/IBmhDH

解决方案

The range initialization line of a for(:) loop does not extend lifetime of anything but the final temporary (if any). Any other temporaries are discarded prior to the for(:) loop executing.

Now, do not despair; there is an easy fix to this problem. But first a walk through of what is going wrong.

The code for(auto x:exp){ /* code */ } expands to, basically:

{
  auto&& __range=exp;
  auto __it=std::begin(__range);
  auto __end=std::end(__range);
  for(; __it!=__end;++__it){
    auto x=*__it;
    /* code */
  }
}

(With a modest lies on the __it and __end lines, and all variables starting with __ have no visible name. Also I am showing C++17 version, because I believe in a better world, and the differences do not matter here.)

Your exp creates a temporary object, then returns a reference to within it. The temporary dies after that line, so you have a dangling reference in the rest of the code.

Fixing it is relatively easy. To fix it:

std::string const& func() const& // notice &
{
    return m.find("key")->second;
}
std::string func() && // notice &&
{
    return std::move(m.find("key")->second);
}

do rvalue overloads and return moved-into values by value when consuming temporaries instead of returning references into them.

Then the

auto&& __range=exp;

line does reference lifetime extension on the by-value returned string, and no more dangling references.

As a general rule, never return a range by reference to a parameter that could be an rvalue.


Appendix: Wait, && and const& after methods? rvalue references to *this?

C++11 added rvalue references. But the this or self parameter to functions is special. To select which overload of a method based on the rvalue/lvalue-ness of the object being invoked, you can use & or && after the end of the method.

This works much like the type of a parameter to a function. && after the method states that the method should be called only on non-const rvalues; const& means it should be called for constant lvalues. Things that don't exactly match follow the usual precidence rules.

When you have a method that returns a reference into an object, make sure you catch temporaries with a && overload and either don't return a reference in those cases (return a value), or =delete the method.

这篇关于为什么在基于范围的初始化程序中使用临时对象会导致崩溃?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆