71

An argument to this function will bind to an rvalue reference:

void f(int && i);

However, an argument to this function will bind to either an rvalue or an lvalue reference:

template <typename T>  
void f(T && t);

I've often heard this referred to as a universal reference.
I've also heard it been called a forwarding reference.
Do they mean the same thing?
Is it only a forwarding reference if the function body calls std::forward?

Barry
  • 286,269
  • 29
  • 621
  • 977
Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271

2 Answers2

83

Do they mean the same thing?

Universal reference was a term Scott Meyers coined to describe the concept of taking an rvalue reference to a cv-unqualified template parameter, which can then be deduced as either a value or an lvalue reference.

At the time the C++ standard didn't have a special term for this, which was an oversight in C++11 and makes it hard to teach. This oversight was remedied by N4164, which added the following definition to [temp.deduct]:

A forwarding reference is an rvalue reference to a cv-unqualified template parameter. If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.

Hence, the two mean the same thing, and the current C++ standard term is forwarding reference. The paper itself articulates why "forwarding reference" is a better term than "universal reference."

Is it only a forwarding reference if the function body calls std::forward?

Nope, what you do with a forwarding reference is irrelevant to the name. The concept forwarding reference simply refers to how the type T is deduced in:

template <class T> void foo(T&& ); // <== 

It does not need to be subsequently forwarded .

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 3
    Nice answer! Do you have any (non-trivial) example where you won't invoke a `std::forward` on a forwarding reference? You can also mention the `auto&&` case for local variables. – vsoftco Sep 17 '16 at 22:42
  • 3
    @vsoftco `template void foo(F&& arg) { use(arg); use_again(arg); consume(std::forward(arg)); }` – Barry Sep 17 '16 at 22:47
  • Self-explanatory, thanks, although you still end up with a forward :) – vsoftco Sep 17 '16 at 22:48
  • @vsoftco `std::shuffle`, for instance. – T.C. Sep 19 '16 at 21:15
  • @T.C. Thanks! Can you explain why it's not forwarded? So you don't "accidentally" end up moving the RNG? But then why passing by forwarding ref? Perhaps I should ask this as a new question... – vsoftco Sep 19 '16 at 23:48
  • @vsoftco Basically, because it's used truly as a "universal reference" (to accept both lvalue and rvalue URNGs) and not for forwarding. See [LWG1432](http://wg21.link/LWG1432). – T.C. Sep 20 '16 at 02:37
  • Reminder: a type is "cv-unqualified" if it is neither const nor volatile. see https://stackoverflow.com/questions/15413037/what-does-cv-unqualified-mean-in-c – luca Dec 06 '21 at 17:14
  • @vsoftco Yes, a simple range-based for loop for example. I'm not really sure why they felt compelled to use the term *forwarding reference* instead of the well-established term *universal reference* that everyone was using anyway. – 303 Nov 24 '22 at 01:58
  • @303 My answer links to the paper which introduced forwarding reference, which provides an answer for "why they felt compelled to" introduce it. You can agree or disagree with the argument put forward there, but you don't have to be unsure of why. – Barry Nov 28 '22 at 03:08
19

Unfortunately, it's confusing, but they are nothing more than two names for the same thing.
Universal reference was proposed (I guess) by Meyers far ago (see here as an example).
Forwarding reference is picked up directly from the standardese. That's all.

skypjack
  • 49,335
  • 19
  • 95
  • 187