The range-based for is specifically intended to replace loops akin to the following (this is a somewhat simplistic case; range-based for, especially the C++17 version, is more general than the example):
for (auto it = range.begin(), end = range.end(); it != end; ++it) {
use(*it);
}
In the majority of cases won't use the values at the different locations but will rather use the element at the location itself:
- When mutating the elements in the sequence a value doesn't really help.
- In most cases copying the values is expensive and keeping a reference is more effective.
- There are even cases where objects can't be copied to start with.
As a result the designers of range-based for decided that references absolutely have to be supported. At the same time, it was intended to use a reasonably simplistic rewrite-rule for a range-based for. The rule which is codified in the standard is this:
for (<range-decl>: <range>) { <body> }
is equivalent to
{
auto&& range = <range>; // keep the range alive!
auto it = begin(range); // actually, reality is bit more complicated
auto end = end(range); // actually, reality is a bit more complicated
for (; it != end; ++it) {
<range-decl> = *it; // this is the rewrite causing your issue
<body>
}
}
In particular, the implication is that <range-decl> is a declaration rather than just naming a variable. The reason for this requirement is that typically the entity used in front of the : is a reference. However, references cannot be rebound. However, in each iteration of a loop a new reference can be used.
In principle the rewrite rule could work with using assignments if the <range-decl> isn’t a declaration but rather an lvalue. That would yield its own share of odd behaviors:
- There would be a difference between
for (T const& x: range) and T const& x = 0; for (x: range): the former works while the latter is an error.
- If the lvalue is a reference to an object located somewhere (
T& x = get_reference(); for (x: range) {...}) the loop would automatically assign all the values in a range to an object located somewhere. Normally the objects are either located on the stack or in the source range (when the variable is declared as a reference).
It was consider more reasonable to only allow initialisations than supporting initialisation or assignments depending on how the variable is declared. Looking at the revision history of the proposals (N2930 and predecessors) doesn’t yield a discussion but I vaguely recall that ths point was discussed.
forrequires a declaration rather than merely naming a variable. ... and I'd be surprised if typical textbooks cover the rational for this oddity.int i; for(i = 0; i < 10; ++i)used. Not such a big leap to try that on a range-based for as well.forand it is actually illogical that it is not supported! There is a reason but I think this reason may even be somewhat feeble...