50

Assuming my current rule when programming with range-based loops says

Use for(auto const &e :...) or for(auto &e:...) when possible over for(auto a: ...).

I base this on my own experience and this question for example.

But after reading about the new terse for loops I wonder, should I not replace my & in my rule with &&? As written here this looks like the Meyers' Universal References.

So, I ask myself, should my new rule either be

Use for(auto const &&e :...) or for(auto &&e:...) when possible ...

or does that not always work and therefore should rather be the quite complicated one

Check if for(auto const &&e :...) or for(auto &&e:...) is possible, then consider for(auto const &e :...) or for(auto &e:...), and only when needed do not use references.

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
towi
  • 21,587
  • 28
  • 106
  • 187
  • 1
    `auto &&` always works. That's why it's "universal". If necessary, the deduced type will be qualified. – Kerrek SB Nov 18 '14 at 10:00
  • 3
    `const auto&&` is not a forwarding reference, it's const rvalue reference – Piotr Skotnicki Nov 18 '14 at 10:06
  • 1
    and the rest is explained in [N3853](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3853.htm) and [What is the advantage of using universal references in range-based for loops?](http://stackoverflow.com/questions/13130708/what-is-the-advantage-of-using-universal-references-in-range-based-for-loops) – Piotr Skotnicki Nov 18 '14 at 10:10
  • I think it was Herb Sutter in "Back to basics" who said: writing `const auto&&` is almost always wrong. – Bulletmagnet Nov 18 '14 at 11:35
  • @Bulletmagnet I found the talk slides [https://github.com/CppCon/CppCon2014/tree/master/Presentations/Back%20to%20the%20Basics!%20Essentials%20of%20Modern%20C%2B%2B%20Style] but I can not find your reference. He says "for local variables", but I think in "auto for loops" it maay be an exception. Do you have the exact or better reference? – towi Nov 18 '14 at 12:14
  • I could be wrong, maybe it was Scott Meyers in "Type deduction and why you care". – Bulletmagnet Nov 18 '14 at 12:17
  • @Bulletmagnet Well, I'll buy Scott's new book soon anyway. But I have his old C++11 slides and I don't think he mentions it there. I can follow Herbs argument about local variables, but I wonder auto-for is not just an exception then -- or why N3853 seems to disagree. – towi Nov 18 '14 at 12:24
  • 2
    @Bulletmagnet it was STL during [CppCon'14](https://www.youtube.com/watch?v=dTeKf5Oek2c&t=47m4s) – Piotr Skotnicki Nov 18 '14 at 12:27
  • @PiotrS. No, it is obviously not a "forwarding reference" as in `template void func(T&&)`. But it seems to me that "reference collapsing rules" do apply here, still. So I assumed one can call this "universal reference", informally. Am I wrong? – towi Nov 18 '14 at 12:27
  • @Bulletmagnet Thanks, STL was it, alright. Now I got it, you refer to the `const`, mainly. I missed that. Of course, thank you. – towi Nov 18 '14 at 12:30
  • @PiotrS. Thank you that is a very important information. I was under a misconception, then. That definitely makes the difference. – towi Nov 18 '14 at 12:37
  • @towi only `auto&&`, with **no** qualifiers, is universal/forwarding reference. None of the following are universal references: `const auto&&`, `volatile auto&&`, `const volatile auto&&`, `auto*&&`, and so forth... – Piotr Skotnicki Nov 18 '14 at 12:46
  • 2
    @PiotrS. Cunningham's Law states "the best way to get the right answer on the Internet is not to ask a question, it's to post the wrong answer." – Bulletmagnet Nov 18 '14 at 14:08
  • @towi no, it only states that `auto` used as a type specifier uses the same rules as template argument deduction procedure, in other words `const auto&& = x;` is the same as `template void f(const T&& t); f(x);`. – Piotr Skotnicki Nov 19 '14 at 08:31
  • @PiotrS. Yes, I know, but doesn't exactly your example imply that it behaves like a F/URef? {Damn, how do I move this comment thread to a wiki discussion? :-) } – towi Nov 19 '14 at 08:34
  • @towi no, because neither `T` in `template void f(const T&&)` is a forwarding reference, nor `const auto&&` is. The fact that `T&&` occurs in parameter declaration does not imply it is forwarding reference. Only *pure* `T&&` with **no qualifiers** like `const` or `volatile` is forwarding reference, meaning it has to be `template void f(T&&)` or `auto&&`, and ***never*** `const T&&` or `const auto&&` – Piotr Skotnicki Nov 19 '14 at 08:39
  • @PiotrS. Alright, I believe you because you state it so firmly. But I would feel better about it, if you could point me to a reference (no pun intended). – towi Nov 19 '14 at 08:43
  • @towi: §14.8.2.1 [temp.deduct.call]/p3. you seem to still be misleading the difference between `auto&` and `auto&&`, as well as `const auto&` and `const auto&&`. they have completely different meaning. – Piotr Skotnicki Nov 19 '14 at 10:36
  • Possible duplicate of [What is the advantage of using universal references in range-based for loops?](https://stackoverflow.com/questions/13130708/what-is-the-advantage-of-using-universal-references-in-range-based-for-loops) – mskfisher Feb 27 '19 at 15:56

1 Answers1

15

When and if you should use auto&& in for loops has been explained very nicely by Howard Hinnant here.

This leaves the question what x in

auto &&x = ...expr...

actually is. And it is handled as if there there were a function template definition

template <class U> void f(U&& u);

and the type of x is deduced by the same rules as u [§7.1.6.4.(7)].

This means it is not handled as a RValue Reference, but as a "Universal/Forwarding Reference" -- the "Reference Collapsing Rules" apply.

This also holds for

const auto &&x = ...expr...

as the example in §7.1.6.4.(7) states, at least for const auto &x.

But, as PiotrS says in the questions comments, any qualifiers nullifies the URef-ness:

no, because neither T in template<class T> void f(const T&&) is a forwarding reference, nor const auto&& is. The fact that T&& occurs in parameter declaration does not imply it is forwarding reference. Only pure T&& with no qualifiers like const or volatile is forwarding reference, meaning it has to be template<class T> void f(T&&) or auto&&, and never const T&& or const auto&&

Enlico
  • 23,259
  • 6
  • 48
  • 102
towi
  • 21,587
  • 28
  • 106
  • 187
  • 2
    For Forward reference, it should be `template void f(U&& u);` not `template void f(const U& u);` – abhiarora May 09 '20 at 08:23
  • "nullifies the URef-ness" is highly misleading... reference collapsing happens for `const T&&` just as it does for `T&&`. It is no longer "universal" because half the types in the universe (the ones not `const` qualified) cannot be matched, but it behaves much the same. – Ben Voigt Oct 20 '20 at 15:00
  • @BenVoigt It's not just qualification. A `const auto&&` declarator or the `u` in `template void f(const U&& u);` can never automatically deduce type from any lvalue. The special rule which makes it valid for a true forwarding reference (and indirectly for the exact type `auto&&`) no longer applies. – aschepler Jan 30 '21 at 20:52