Reference declaration
Declares a named variable as a reference, that is, an alias to an already-existing object or function.
Syntax
A reference variable declaration is any simple declaration whose declarator has the form
& attr (optional) declarator
|
(1) | ||||||||
&& attr (optional) declarator
|
(2) | (since C++11) | |||||||
D
as an lvalue reference to the type determined by decl-specifier-seq
S
D
as an rvalue reference to the type determined by decl-specifier-seq
S
declarator | - | any declarator except another reference declarator (there are no references to references) |
attr | - | (since C++11) list of attributes |
A reference is required to be initialized to refer to a valid object or function: see reference initialization.
The type “reference to (possibly cv-qualified) void” cannot be formed.
Reference types cannot be cv-qualified at the top level; there is no syntax for that in declaration, and if a qualification is added to a typedef-nameor decltype
specifier,(since C++11) or type template parameter
References are not objects; they do not necessarily occupy storage, although the compiler may allocate storage if it is necessary to implement the desired semantics (e.g. a non-static data member of reference type usually increases the size of the class by the amount necessary to store a memory address).
Because references are not objects, there are no arrays of references, no pointers to references, and no references to references:
int& a[3]; // error int&* p; // error int& &r; // error
Reference collapsingIt is permitted to form references to references through type manipulations in templates or typedefs, in which case the reference collapsing typedef int& lref; typedef int&& rref; int n; lref& r1 = n; // type of r1 is int& lref&& r2 = n; // type of r2 is int& rref& r3 = n; // type of r3 is int& rref&& r4 = 1; // type of r4 is int&& (This, along with special rules for template argument deduction when |
(since C++11) |
Lvalue references
Lvalue references can be used to alias an existing object (optionally with different cv-qualification):
#include <iostream> #include <string> int main() { std::string s = "Ex"; std::string& r1 = s; const std::string& r2 = s; r1 += "ample"; // modifies s // r2 += "!"; // error: cannot modify through reference to const std::cout << r2 << '\n'; // prints s, which now holds "Example" }
They can also be used to implement pass-by-reference semantics in function calls:
#include <iostream> #include <string> void double_string(std::string& s) { s += s; // 's' is the same object as main()'s 'str' } int main() { std::string str = "Test"; double_string(str); std::cout << str << '\n'; }
When a function's return type is lvalue reference, the function call expression becomes an lvalue expression:
#include <iostream> #include <string> char& char_number(std::string& s, std::size_t n) { return s.at(n); // string::at() returns a reference to char } int main() { std::string str = "Test"; char_number(str, 1) = 'a'; // the function call is lvalue, can be assigned to std::cout << str << '\n'; }
Rvalue referencesRvalue references can be used to extend the lifetimes Run this code #include <iostream> #include <string> int main() { std::string s1 = "Test"; // std::string&& r1 = s1; // error: can't bind to lvalue const std::string& r2 = s1 + s1; // okay: lvalue reference to const extends lifetime // r2 += "Test"; // error: can't modify through reference to const std::string&& r3 = s1 + s1; // okay: rvalue reference extends lifetime r3 += "Test"; // okay: can modify through reference to non-const std::cout << r3 << '\n'; } More importantly, when a function has both rvalue reference and lvalue reference overloads Run this code #include <iostream> #include <utility> void f(int& x) { std::cout << "lvalue reference overload f(" << x << ")\n"; } void f(const int& x) { std::cout << "lvalue reference to const overload f(" << x << ")\n"; } void f(int&& x) { std::cout << "rvalue reference overload f(" << x << ")\n"; } int main() { int i = 1; const int ci = 2; f(i); // calls f(int&) f(ci); // calls f(const int&) f(3); // calls f(int&&) // would call f(const int&) if f(int&&) overload wasn't provided f(std::move(i)); // calls f(int&&) // rvalue reference variables are lvalues when used in expressions int&& x = 1; f(x); // calls f(int& x) f(std::move(x)); // calls f(int&& x) } This allows move constructors, move assignment operators, and other move-aware functions (e.g. std::vector::push_back() Because rvalue references can bind to xvalues, they can refer to non-temporary objects: int i2 = 42; int&& rri = std::move(i2); // binds directly to i2 This makes it possible to move out of an object in scope that is no longer needed: std::vector<int> v{1, 2, 3, 4, 5}; std::vector<int> v2(std::move(v)); // binds an rvalue reference to v assert(v.empty()); Forwarding referencesForwarding references are a special kind of references that preserve the value category of a function argument, making it possible to forward it by means of std::forward
1) function parameter of a function template declared as rvalue reference to cv-unqualified type template parameter
template<class T> int f(T&& x) // x is a forwarding reference { return g(std::forward<T>(x)); // and so can be forwarded } int main() { int i; f(i); // argument is lvalue, calls f<int&>(int&), std::forward<int&>(x) is lvalue f(0); // argument is rvalue, calls f<int>(int&&), std::forward<int>(x) is rvalue } template<class T> int g(const T&& x); // x is not a forwarding reference: const T is not cv-unqualified template<class T> struct A { template<class U> A(T&& x, U&& y, int* p); // x is not a forwarding reference: T is not a // type template parameter of the constructor, // but y is a forwarding reference };
2)
auto&& except when deduced from a brace-enclosed initializer list
or, when representing a template parameter of a class template during class template argument deduction
(since C++17)
auto&& vec = foo(); // foo() may be lvalue or rvalue, vec is a forwarding reference auto i = std::begin(vec); // works either way (*i)++; // works either way g(std::forward<decltype(vec)>(vec)); // forwards, preserving value category for (auto&& x: f()) { // x is a forwarding reference; this is a common way to use range for in generic code } auto&& z = {1, 2, 3}; // *not* a forwarding reference (special case for initializer lists) See also template argument deduction and std::forward |
(since C++11) |
Dangling references
Although references always refer to valid objects or functions upon initialization, it is possible to create a program where the lifetime of the referred-to object ends, but the reference remains accessible (dangling
Given an expression expr of reference type and let target
- If a pointer to target would be valid in the context of the evalution of expr, the result designates target
- Otherwise, the behavior is undefined.
std::string& f() { std::string s = "Example"; return s; // exits the scope of s: // its destructor is called and its storage deallocated } std::string& r = f(); // dangling reference std::cout << r; // undefined behavior: reads from a dangling reference std::string s = f(); // undefined behavior: copy-initializes from a dangling reference
Note that rvalue references and lvalue references to const extend the lifetimes of temporary objects (see Reference initialization
If the referred-to object was destroyed (e.g. by explicit destructor call), but the storage was not deallocated, a reference to the out-of-lifetime object may be used in limited ways, and may become valid if the object is recreated in the same storage (see Access outside of lifetime
Type-inaccessible references
Attempting to bind a reference to an object where the converted initializer is an lvalue(until C++11) a glvalue(since C++11) through which the object is not type-accessible
char x alignas(int); int& ir = *reinterpret_cast<int*>(&x); // undefined behavior: // initializer refers to char object
Call-incompatible references
Attempting to bind a reference to a function where the converted initializer is an lvalue(until C++11) a glvalue(since C++11) whose type is not call-compatible
void f(int); using F = void(float); F& ir = *reinterpret_cast<F*>(&f); // undefined behavior: // initializer refers to void(int) function
Notes
Feature-test macro | Value | Std | Feature |
---|---|---|---|
__cpp_rvalue_references |
200610L |
(C++11) | Rvalue references |
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 453 | C++98 | it was unclear which object or function a reference cannot be bound to | made clear |
CWG 1510 | C++11 | cv-qualified references could not be formed in the operand of decltype | allowed |
CWG 2550 | C++98 | parameters could have type “reference to void” | disallowed |
CWG 2933 | C++98 | the behavior of accessing dangling references was unclear | made clear |
External links
Thomas Becker, 2013 - C++ Rvalue References Explained |