Range-based for loop on a temporary range [duplicate]

  • A+
Category:Languages

Thanks to some segmentation faults and warnings in valgrind, I found that this code is incorrect and has some sort of dangling reference in the for-range loop.

#include<numeric> #include<vector>  auto f(){     std::vector<std::vector<double>> v(10, std::vector<double>(3));     iota(v[5].begin(), v[5].end(), 0);     return v; }  int main(){     for(auto e : f()[5])         std::cout << e << std::endl;     return 0; } 

It looks as if the begin and end is taken from a temporary and lost in the loop.

Of course, a way around is to do

    auto r = f()[5];     for(auto e : r)         std::cout << e << std::endl; 

However, I wonder exactly why for(auto e : f()[5]) is an error and also if there is a better way around or some way to design f or the even the container (std::vector) to avoid this pitfall.

With iterator loops is more obvious why this problem happens (begin and end come from different temporary objects)

for(auto it = f()[5].begin(); it != f()[5].end(); ++it) 

But in a for-range loop, as in the first example, it seems very easy to make this mistake.

 


Note that using a temporary as the range expression directly is fine, its lefetime will be extended. But for f()[5], what f() returns is the temporary and it's constructed within the expression, and it'll be destroyed after the whole expression where it's constructed.

From C++20, you can use init-statement for range-based for loop to solve such problems.

(emphasis mine)

If range_expression returns a temporary, its lifetime is extended until the end of the loop, as indicated by binding to the rvalue reference __range, but beware that the lifetime of any temporary within range_expression is not extended.

This problem may be worked around using init-statement:

for (auto& x : foo().items()) { /* .. */ } // undefined behavior if foo() returns by value for (T thing = foo(); auto& x : thing.items()) { /* ... */ } // OK 

e.g.

for(auto thing = f(); auto e : thing[5])     std::cout << e << std::endl; 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: