1

I'm still confused by the rules invented to support moving and forwarding. One thing I'm still not sure about is:

Is a forwarding reference just an rvalue reference (with reference collapsing rules applied)?

If it is an rvalue reference, then why does the function:

template<typename T>
void func(T&&);

accept not only rvalues, but also lvalues?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
beginpluses
  • 497
  • 2
  • 9
  • 1
    `T&&`, in a **deduced context**, matches anything. – sp2danny Jul 01 '19 at 21:21
  • So it is NOT an rvalue reference? – beginpluses Jul 01 '19 at 21:24
  • it will be either a rvalue reference, or a regular reference, due to reference collapsing rules. cv-ness is also preserved – sp2danny Jul 01 '19 at 21:26
  • 1
    this will be helpful, is you have the patience to read it all https://stackoverflow.com/questions/3582001/advantages-of-using-forward – sp2danny Jul 01 '19 at 21:28
  • 1
    Perhaps https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers ? – Severin Pappadeux Jul 01 '19 at 21:31
  • 1
    Maybe this helps: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4164.pdf – Amadeus Jul 01 '19 at 21:31
  • The Scott Meyers article linked by @Amadeus directly addresses what this question seems to be about. It's worth noting that since it was written, the term "forwarding reference" has come up as almost a synonym to Meyers' "universal reference", though "forwarding reference" is now the term actually used in the C++ Standard. – aschepler Jul 01 '19 at 21:37
  • @aschepler The Scott Meyers article was linked by Severin Pappdeux. Mine is from Herb Sutter :P – Amadeus Jul 01 '19 at 21:39
  • @Amadeus Indeed, oops. I did mean the Meyers article. – aschepler Jul 01 '19 at 21:43

2 Answers2

1

Before T is substituted, T && is an rvalue reference (obviously).

After T is substituted (and after references are collapsed), T && either remains an rvalue reference (if T is not a reference), or becomes an lvalue reference (if T is an lvalue reference).

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • ITYM "Before `T` is substituted, `T&&` is an rvalue reference." `T` itself would often be a non-reference type, e.g. if the expression is a non-string non-function literal or calls a function returning a non-reference type. – aschepler Jul 01 '19 at 21:54
  • @aschepler Indeed, by "it" I meant `T &&`. Edited for clarity. – HolyBlackCat Jul 01 '19 at 21:55
  • "if T is not a reference"? No, "if T is not an lvalue reference"! – Deduplicator Jul 02 '19 at 01:20
  • @Deduplicator `T` would never be deduced as an rvalue reference if `T &&` is a forwarding reference. – HolyBlackCat Jul 02 '19 at 08:18
  • @HolyBlackCat [See it for yourself](http://coliru.stacked-crooked.com/a/b70ebef027384f58). Reference-collapsing collapses `&& &&` and `&&` to `&&`, none to none, and everything else to `&`. – Deduplicator Jul 02 '19 at 08:21
  • @Deduplicator You don't have any forwarding references in your code (because in your code `T` is not being deduced). My answer is specifically about forwarding references, not about reference collapsing in general. – HolyBlackCat Jul 02 '19 at 08:45
  • @HolyNlackCat But why does substitution happen when we pass lvalue? That must cause the compiler error "There is no function func taking an lvalue" – beginpluses Jul 02 '19 at 19:42
  • @beginpluses That's just how the language works. If you initialize `T &&` with an lvalue an let the compiler deduce `T`, it will be deduced as an lvalue reference (so `T &&` also becomes an lvalue reference). You couldn't have forwarding references without this rule. – HolyBlackCat Jul 02 '19 at 19:55
1

I'm not sure if this answer will satisfy you, but I can point out the relevant parts of the standard. In a nutshell, the reference T&& is "grammatically" always an rvalue reference, but sometimes the type it ends up declaring is an lvalue reference type.

When this happens as the result of template argument deduction, the entire construct is called "forwarding reference", as a convenient shorthand. (This circumstance requires reference collapsing, but template argument deduction is not the only time reference collapsing happens.)

Now, on to the standard wording. First we have [dcl.ref] (e.g. p2, p6):

A reference type that is declared using & is called an lvalue reference, and a reference type that is declared using && is called an rvalue reference. [...]

If a typedef-name (9.1.3, 13.1) or a decltype-specifier (9.1.7.2) denotes a type TR that is a reference to a type T, an attempt to create the type “lvalue reference to cv TR” creates the type “lvalue reference to T”, while an attempt to create the type “rvalue reference to cv TR” creates the type TR. [Note: This rule is known as reference collapsing. — end note]

Finally, the case of template argument deduction is handled in [temp.deduct.call]p3:

A forwarding reference is an rvalue reference to a cv-unqualified template parameter [...]

In other words, a forwarding reference is an rvalue reference, but it's one that accepts lvalues, too. (Note that the standard's definition of "forwarding reference" doesn't actually require the template argument to be deduced, although that is the primary way in which you would usually want to trigger the reference collapsing behaviour.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I would include the sentence after the definition of "forwarding reference", which is the remaining key piece, in a sense the opposite of reference collapsing, to make forwarding reference deduction actually work. Though it does presume knowing what `P` and `A` are in the section's context. – aschepler Jul 01 '19 at 21:49
  • @aschepler: maybe, though the question wasn't really how forwarding works, but rather about the terminology that's used for the various constructs in question, which I believe are covered by the above. – Kerrek SB Jul 01 '19 at 21:51
  • @Kerrek SB Thank you very much! But... "a forwarding reference is an rvalue reference, but it's one that accepts lvalues, too." It sounds weird... How can it be? – beginpluses Jul 02 '19 at 19:55