I think part of the confusion arises due to the use of auto
, which is common although not mandatory. Basically the range-based for for (type var : range)
means: iterate over the elements in range
creating a local variable var
of type type
initialized with the respective element in the range
. This construct has a direct translation in term of the regular for loop, mandated by the standard:
// for (for-range-declaration : range-init)
// statement
{
auto && __range = range-init;
for ( auto __begin = begin-expr, __end = end-expr;
__begin != __end; ++__begin ) {
for-range-declaration = *__begin;
statement
}
}
The range-init and for-range-declaration can be anything that can be replaced on the expanded version and compile. There is no requirement on what the type in the for-range-declaration is, and it can even be something different from what the range-init holds:
// Convoluted example:
void printFloor(std::vector<double> const & v) {
for (int i : v) {
std::cout << ' ' << i;
}
}
Because any type can be used that allows for the expansion to compile, auto
is allowed, with exactly the same semantics that it has in any other context, and the behavior is exactly the same as in all other situations.
Changing the requirements for the range-based for to only allow iterating by reference would unnecessarily complicate the language (standard wording) and the implementation (the compiler cannot just expand, as the for-range-declaration would not be just a declaration, but a limited form that inhibited the use of values) and actually limit the uses of the construct requiring more complex user code when a value is needed (the user would have to manually copy). Keep in mind that this construct is there only to simplify the code, it is not an enabling feature, there is nothing that can be done with this construct that cannot be done without it (you can always manually produce the expansion above).