Why does using a temporary object in the database for initialization fail?

Why is the following code broken down in both Visual Studio and GCC?

For its disaster recovery, a range for the loop, std :: map, std :: string and a link to the string are required. If I remove any 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; } 

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

+47
c ++ c ++ 11
Dec 04 '16 at 1:46
source share
2 answers

The for(:) loop range initialization line does not extend the life of anything other than the last temporary (if any). Any other temporary files are discarded before the for(:) loop is executed.

Now, do not despair; There is an easy solution to this problem. But first go through what goes wrong.

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

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

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

Your exp creates a temporary object and then returns a link inside it. Temporary dies after this line, so in the rest of the code you have a dangling link.

Fixation is relatively simple. To fix this:

 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 values ​​by value when using temporary times, instead of returning references to them.

Then

 auto&& __range=exp; 
Line

refers to the extension of the life cycle to the return value of string and no longer wraps links.

As a rule, never return a range by reference to a parameter, which may be the value of r.




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

C ++ 11 added rvalue links. But this or self function is special for functions. To select which method overload is based on the rvalue / lvalue-ness of the called object, you can use & or && after the method finishes.

This works the same as the parameter type for a function. && after the method states that the method should only be called on non-constant values; const& means that it must be called for constant lvalues. Things that do not exactly match correspond to the usual rules of compliance.

When you have a method that returns a link to an object, make sure that you catch temporary files with the && overload and either do not return the link in these cases (return a value) or the =delete method.

+61
Dec 04 '16 at 3:21
source share
 S().func() 

This creates a temporary object and calls a method that returns a reference to std::string belonging (indirectly) to the temporary object ( std::string is in the container that is part of the temporary object).

After receiving the link, the temporary object will be destroyed. It also destroys std::string , which belonged to (indirectly) a temporary object.

After this point, further use of the reference object becomes undefined. Such as content iteration.

This is a very common mistake when it comes to using range iteration. Respectfully, you are also to blame for having dealt with this.

+30
Dec 04 '16 at 1:52
source share



All Articles