Understanding && in C++: Rvalue References, Forwarding References, and Their Use as Function Parameters
The article explains the different meanings of the && operator in C++—as a true rvalue reference in non‑template functions, as a forwarding (universal) reference in templates, and its behavior when binding to function return values—illustrated with code examples and practical guidelines.
C++11 introduced many new language features, among which the && operator often confuses beginners because it does not always denote an rvalue reference.
Scott Meyers famously noted that "T&& doesn't always mean 'rvalue reference'".
&& as a Function Parameter
In non‑template functions, && always represents an rvalue reference, so the function can only accept rvalue arguments.
class A {
...
};
void foo(A&& a) {
...
}The foo function can be called with rvalues such as:
foo(A{});
A a;
foo(std::move(a));
A get_a() {
A a;
...
return a;
}
foo(get_a());Calling it with an lvalue reference fails to compile:
A a1;
A& ar = a1;
foo(ar); // ERRORIn template functions, however, && does not necessarily mean an rvalue reference. When used as T&& in a deduced context, it is a forwarding reference (also called a universal reference), which can bind to both lvalues and rvalues.
template
void bar(T&& t) {
...
}Consequently, bar can accept a wide variety of arguments:
bar(A{});
A a;
bar(std::move(a));
bar(get_a());
A a1;
A& ar1 = a1;
bar(ar1);
const A& ar2 = a1;
bar(ar2);Not all && in templates are forwarding references. For example, a parameter of type vector && can only bind to rvalues:
template
void bar(vector
&& tv) {
...
}Similarly, adding const to a deduced && makes it a true rvalue reference:
template
void bar(const T&& v) {
...
}&& When Receiving Return Values
The confusion also appears when binding the result of a function call. Declaring a variable as A&& a = test1(); makes a an rvalue reference, while using auto&& a = test2(); creates a forwarding reference that can bind to any value category.
class A {
...
};
A&& a = test1(); // rvalue reference
auto&& a = test2(); // forwarding (universal) referenceIf the type is explicitly specified (e.g., A&& ), it remains an rvalue reference and can only accept rvalues.
Understanding these distinctions helps avoid compilation errors and makes advanced C++ idioms like perfect forwarding clearer.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.