In pre-Range TS code, I might do something like the following to get a (potentially modifiable) reference to a ForwardIterator's value:
auto &val = *it;
This would also be used in range-based for
loops over such iterators:
for(auto &val : some_range)
However, in C++20 and Range TS-based code, I'm seeing a lot of auto&&
usage in these locations. I understand what auto&&
is doing from a language standpoint. What I don't understand is why it's being used in these places, when auto&
ought to work just fine? Most code of this nature isn't forwarding the reference, so why is it using a forwarding reference to capture it?
This idiom is being used because the Range TS and the C++20-equivalent actually requires this idiom. The reason for this requirement has to do with the fact that proxy iterators are now legal constructs for all categories of iterators.
A proxy iterator's operator*
does not return a value_type&
; it returns a prvalue of an object that acts like an reference. Therefore, if you tried to store a reference to the value with auto&
, your code would fail to compile with a proxy iterator.
The Range TS's iterator concepts never require that the return value from operator*
is actually a language reference. The Readable concept only requires that the return value is convertible to a value_type
. The Writable concept only requires that a value_type
can be assigned to the return value.
So code getting references from iterators of all kinds under the Range feature must now use auto&&
, which can bind to prvalues without making them const
. So when dealing with ranges, if you want to get a possibly non-const reference to the value, auto&&
is the most convenient way. If you want just a const
reference, you can continue to use const auto &
.
Note that auto&&
also handles iterators that directly return prvalues of type value_type
from their operator*
. Obviously these are only useful for Readable iterators, but they're quite useful. The simplest example would be an iterator for a counting range over some number of integers. The iterator returns an int
, not a reference to some memory. Such iterators can be RandomAccessIterators in the Range TS due to not having a hard requirement for returning a language reference.
Such iterators would also make sense for regular expression matching. The current regex_iterator
returns a reference to an internal object; that's necessary for being a ForwardIterator. Instead, it could have been much smaller by having operator*
do the search and return the match object as a prvalue. But the old system wouldn't allow that.
Given such an iterator, auto&
cannot be used to capture "references" to their results.
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