I have the following code, why is the range based output is not what is stored in the vector?
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
shared_ptr<vector<double>> get_ptr() {
vector<double> v{1, 2, 3, 4, 5};
return make_shared<vector<double>>(v);
}
int main() {
auto vec = get_ptr();
for (int i = 0; i<get_ptr()->size(); ++i) {
std::cout << (*get_ptr())[i] << std::endl;
}
for (auto v: (*get_ptr())) {
std::cout << v << std::endl;
}
}
The output on my ubuntu is something like below,
1
2
3
4
5
4.67913e-310
4.67913e-310
3
4
5
3
Answers
Every time you call
get_ptr()
you create a new and unique copy of the vector. And that object will be destructed as soon as the full expression involving the call is finished.So in the case of
As soon as
*get_ptr()
is finished, the object will be destructed.And due to how ranged for loops works, you will iterate using a non-existing vector, leading to undefined behavior.
Your code has undefined behavior, range-based for loop is equivalent as
For
auto && __range = range-expression ;
, in your code it’ll beauto && __range = (*get_ptr()));
.get_ptr()
returns by-value, what it returns is a temporary which will be destroyed after the full expression. After that__range
is dangling. Any dereference on it leads to UB.Even for the 1st code snippet,
get_ptr()
creates newvector
every time when it’s called inif
condition and every iteration. It works just becauseget_ptr()
always createsvector
containing same elements and accessing them by index is fine.Since C++20 we can use temporary range expression for such temporary lifetime issue.
A range-for loop behaves as if initializing the range to iterate over by a declaration of the form
where
range-expression
is the expression after the:
. In your case:get_ptr
returns by-value and therefore this creates a temporarystd::shared_ptr
holding the vector. You then dereference it andrange
will reference the managed vector. However, a temporary lives only until the end of the full-expression in which it was created and therefore thestd::shared_ptr
returned from the function is immediately after this line destroyed and it will take the managed vector with it since there is no otherstd::shared_ptr
referring to it.So the actual loop then uses the
range
reference which is now however dangling, causing undefined behavior.Since C++20 there is an alternative syntax for range-for loops which allows you to store the temporary return value for the purpose of the loop, avoiding this common pitfall.