27

A range-based for statement is defined in §6.5.4 to be equivalent to:

{
  auto && __range = range-init;
  for ( auto __begin = begin-expr,
             __end = end-expr;
        __begin != __end;
        ++__begin ) {
    for-range-declaration = *__begin;
    statement
  }
}

where range-init is defined for the two forms of range-based for as:

for ( for-range-declaration : expression )         =>   ( expression )
for ( for-range-declaration : braced-init-list )   =>   braced-init-list

(the clause further specifies the meaning of the other sub-expressions)

Why is __range given the deduced type auto&&? My understanding of auto&& is that it's useful for preserving the original valueness (lvalue/rvalue) of an expression by passing it through std::forward. However, __range isn't passed anywhere through std::forward. It's only used when getting the range iterators, as one of __range, __range.begin(), or begin(__range).

What's the benefit here of using the "universal reference" auto&&? Wouldn't auto& suffice?

Note: As far as I can tell, the proposal doesn't say anything about the choice of auto&&.

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324

1 Answers1

32

Wouldn't auto& suffice?

No, it wouldn't. It wouldn't allow the use of an r-value expression that computes a range. auto&& is used because it can bind to an l-value expression or an r-value expression. So you don't need to stick the range into a variable to make it work.

Or, to put it another way, this wouldn't be possible:

for(const auto &v : std::vector<int>{1, 43, 5, 2, 4})
{
}

Wouldn't const auto& suffice?

No, it wouldn't. A const std::vector will only ever return const_iterators to its contents. If you want to do a non-const traversal over the contents, that won't help.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Oops, I actually meant to ask if `const auto&` would suffice. Won't this bind to anything too? – Joseph Mansfield Nov 05 '12 at 22:10
  • @sftrabbit: It won't bind to a *non-const* l-value. A `const std::vector` will only return `const` iterators. – Nicol Bolas Nov 05 '12 at 22:11
  • Thanks. This means that `auto&&` is also useful for binding to anything without making it `const`, in addition to forwarding. – Joseph Mansfield Nov 05 '12 at 22:19
  • 8
    Just to clarify: `for (const auto& v : std::vector{1, 43, 5, 2, 4}) {}` is possible for a const traversal. – Felix Glas Aug 09 '13 at 11:02
  • But then why would you want to modify the temporary vector you are iterating over inside the loop? – fyzix Feb 19 '18 at 08:55
  • 2
    @fyzix: And what if it's *not* a temporary vector? See, you can't go changing the definition of range-based `for` based on whether the range is a temporary or not. So whatever you pick, `const auto&` or `auto&&` has to be the same in all cases. If you use `const auto&`, you can only ever have `const` iteration for all ranges. If you use `auto&&`, you can have whatever kind of iteration you want. – Nicol Bolas Feb 19 '18 at 14:43
  • *"No, it wouldn't. It wouldn't allow the use of an r-value expression that computes a range."* ->Why is this possible then?: `for(auto &v : std::vector{1, 43, 5, 2, 4}){...}` – starriet Sep 29 '22 at 06:05
  • 1
    @starriet: The `auto&&` in question is built by the range-based `for` mechanism. If range-based `for` *internally* used `auto&`, the code you posted would not compile. It uses `auto&&` to *allow* that code to compile. That's the point of my answer. – Nicol Bolas Sep 29 '22 at 13:27
  • Oh, then I think @FelixGlas's comment above seems to have misunderstood your intention, right? `for (T v : std::vector{1, 43, 5, 2, 4}) {}` would not be possible *regardless of* what `T` is, **if** range-based `for` internally used `auto&`. Reading [§8.6.4 The range-based for statement] of C++ standard document helped me to understand. Please let me know if I didn't understand correctly. Thank you. – starriet Sep 30 '22 at 08:57