A universal reference is a special type of reference in C++ that can bind to both lvalues and rvalues. Universal references are typically used in function templates and are identified by the syntax T&&
when T
is a template parameter and type deduction is in play. Universal references are a powerful tool in C++ that allow you to write generic code that works seamlessly with both lvalues and rvalues.
T&&
is used in a non-template context, it is simply an rvalue reference.Universal references are identified by the combination of two factors:
- Template Parameter Deduction: The reference type must involve a template parameter.
- T&&
Syntax: The reference type must be written as T&&
.
For example, in the function template below, param is a universal reference:
template<typename T>
void func(T&& param) {
// param is a universal reference
}
Let's explore some examples to understand how universal references work in practice.
#include <iostream>
template<typename T>
void func(T&& param) {
if constexpr (std::is_lvalue_reference<T>::value) {
std::cout << "param is an lvalue reference\n";
} else {
std::cout << "param is an rvalue reference\n";
}
}
int main() {
int x = 42;
func(x); // x is an lvalue, so T deduces to int&
func(42); // 42 is an rvalue, so T deduces to int
return 0;
}
func(x)
: x is an lvalue, so T
is deduced to int&
, making param of type int& &&
, which collapses to int&
(an lvalue reference).func(42)
: 42
is an rvalue, so T
is deduced to int
, making param of type int&&
(an rvalue reference).C++ has rules for reference collapsing, which dictate how multiple reference qualifiers combine. The rules are:
- T& &
becomes T&
- T& &&
becomes T&-
T&& &becomes
T&-
T&& &&becomes
T&&`
- These rules ensure that universal references work correctly with both lvalues and rvalues.
Universal references are essential for perfect forwarding, where you want to pass arguments to another function while preserving their value category.
#include <iostream>
#include <utility> // for std::forward
void process(int& x) {
std::cout << "Lvalue reference: " << x << std::endl;
}
void process(int&& x) {
std::cout << "Rvalue reference: " << x << std::endl;
}
template<typename T>
void forwarder(T&& arg) {
process(std::forward<T>(arg)); // Perfectly forward the argument
}
int main() {
int a = 42;
forwarder(a); // Passes as lvalue
forwarder(42); // Passes as rvalue
forwarder(std::move(a)); // Passes as rvalue
return 0;
}
process(int& x)
: This overload is called when an lvalue is passed.process(int&& x)
: This overload is called when an rvalue is passed.forwarder(T&& arg)
: arg is a universal reference. std::forward is used to perfectly forward arg to process, preserving its value category.It's important to differentiate between universal references and rvalue references:
- Rvalue References: Written as T&&
when T
is a concrete type (not a template parameter). Rvalue references can only bind to rvalues.
- Universal References: Also written as T&&
, but T
is a template parameter. Universal references can bind to both lvalues and rvalues, depending on how T
is deduced.
#include <iostream>
// Rvalue reference
void rvalueRef(int&& param) {
std::cout << "Rvalue reference\n";
}
// Universal reference
template<typename T>
void universalRef(T&& param) {
std::cout << "Universal reference\n";
}
int main() {
int x = 42;
// rvalueRef(x); // Error: x is an lvalue
rvalueRef(42); // OK: 42 is an rvalue
universalRef(x); // OK: x is an lvalue
universalRef(42); // OK: 42 is an rvalue
return 0;
}
rvalueRef(int&& param)
: This function only accepts rvalues. Attempting to pass x
(an lvalue) to this function results in an error.universalRef(T&& param)
: This function is a template function with a universal reference. It can accept both lvalues and rvalues, depending on how T
is deduced.T&&
syntax in a templated context.T
is deduced based on whether the argument is an lvalue or an rvalue.Universal references are a powerful feature in C++ that enable the creation of highly generic and efficient code. They are particularly useful in template programming and are foundational to modern C++ programming techniques like perfect forwarding.
Previous Page | Course Schedule | Course Content