Suppose I have a function that returns a std::vector
by value:
std::vector<int> buildVector();
It would seem natural to iterate over the result using a range-based for
:
for (int i : buildVector()) { // ... }
Question: Is it safe to do so?
My reading of the standard (actually, draft n4431) suggests that it might not be, though I'm having a hard time believing that the committee failed to allow for this usage. I'm hoping that my reading is incorrect.
Section 6.5.4 defines the range-based for
:
for ( for-range-declaration : expression ) statement
with the following desugaring:
{ auto && __range = range-init; for ( auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) { for-range-declaration = *__begin; statement } }
where range-init
is just ( expression )
, and at least for class types, begin-expr
is either __range.begin()
or begin(__range)
, etc.
In my buildVector
example, I think the range-init
produces a temporary, which the implementation is allowed to destroy immediately after the __range
reference is bound. This would mean that the __range
reference might already be dangling by the time begin-expr
is evaluated.
Certainly, it should always be safe to write this:
std::vector<int> notATemporary = buildVector(); for (int i : notATemporary) { // ... }
But I'm hoping I don't have to add this to my list of gotchas.
Range-for is as fast as possible since it caches the end iterator[citationprovided], uses pre-increment and only dereferences the iterator once. Then, yes, range-for may be slightly faster, since it's also easier to write there's no reason not to use it (when appropriate).
Range-based for loop in C++ Range-based for loop in C++ is added since C++ 11. It executes a for loop over a range. Used as a more readable equivalent to the traditional for loop operating over a range of values, such as all elements in a container.
C++11 introduced the ranged for loop. This for loop is specifically used with collections such as arrays and vectors. Here, the ranged for loop iterates the array num from beginning to end. The int variable var stores the value of the array element in each iteration.
Yes, it's perfectly safe.
From [class.temporary]/4-5:
There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when a default constructor is called [...]
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
- A temporary bound to a reference member in a constructor’s ctor-initializer [...]
- A temporary bound to a reference parameter in a function call [...]
- The lifetime of a temporary bound to the returned value in a function return statement [...]
- A temporary bound to a reference in a new-initializer [...]
None of those exceptions apply. The temporary thus persists for the lifetime of the reference, __range
, which is the entire loop.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With