Published on

R-value reference vs Universal references


Universal References vs Rvalue References

Let's clear up the confusion between T&& in C++. It's not just about rvalue references; there's another concept called universal references.

What's an Rvalue Reference?

Simply put, an rvalue reference is a reference that can bind to a temporary value. For example:

MyClass&& a = MyClass(); // Here, 'a' is an rvalue reference

What's a Universal Reference?

A universal reference is a type of reference that can bind to both lvalues and rvalues. Check this out:

auto&& a = MyClass(); // Here, 'a' is a universal reference
auto&& b = a; // 'b' also becomes a universal reference

How Do They Differ?

At first glance, they might seem similar, but there's a subtle difference.

Universal references typically appear in template functions like this:

template<typename T>
void foo(T&& args)
    // Do something

In this case, args is a universal reference.

On the other hand, if we have something like this:

template<class T>
class MyClass {
    void foo(T&& args)
        // Do something

Here, args is an rvalue reference.

How can you tell them apart?

It's simple. For something to be a universal reference, two conditions must be met:

  1. The parameter must be in the form T&&. It can also be like Args&& ....
  2. The type of the argument must be deduced when the function is called.

For example: In MyClass::foo(), the type T isn't deduced when calling the function. It's deduced when initializing MyClass. So, args here is an rvalue reference.

But in foo(), the type needs to be deduced, making it a universal reference.

Another example:

template<typename T>
void f(const T&& param); // 'param' is an rvalue reference

Since auto also leads to type deduction, this:

auto&& x = MyClass();

Makes x a universal reference.