Reference declaration
Declares a named variable as a reference, that is, an alias to an already-existing object or function.
Contents |
[edit] 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 type determined by decl-specifier-seq S
.declarator | - | any declarator except another reference declarator (there are no references to references) |
attr(C++11) | - | optional list of attributes |
A declaration of a reference (except for class members and function parameters) is required to contain an initializer: see reference initialization which must identify a valid object or function.
There are no references to void and no references to references.
Reference types cannot be cv-qualified at the top level; there is no syntax for that in declaration, and if a qualification is introduced through a typedef, decltype, or template type argument, it is ignored.
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
It is permitted to form references to references through type manipulations in templates or typedefs, in which case the reference collapsing rules apply: rvalue reference to rvalue reference collapses to rvalue reference, all other combinations form lvalue reference: 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) |
[edit] 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 lifetime of a modifiable temporary (note, lvalue references to const can extend lifetimes too, but they are not modifiable) 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; // OK, lvalue ref to const extends lifetime // r2 += "Test"; // error: can't modify through reference to const std::string&& r3 = s1 + s1; // OK, rvalue ref extends lifetime r3 += "Test"; // this object can be modified std::cout << r3 << '\n'; }
More importantly, when a function has both rvalue reference and lvalue reference overloads, the rvalue reference overload binds to rvalues, while the lvalue reference overload binds to lvalues. Run this code #include <iostream> 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 n = 1; const int x = 2; f(n); // calls f(int&) f(x); // calls f(const int&) f(3); // calls f(int&&) // would call f(const int&) if f(int&&) overload wasn't provided }
This allows move constructors, move assignment operators, and other move-aware functions (e.g. vector::push_back() to be automatically selected when suitable. |
(since C++11) |
[edit] Dangling references
Although references, once initialized, always refer to valid objects or functions, it is possible to create a program where the lifetime of the referred-to object ends, but the reference remains accessible (dangling). Accessing such reference is undefined behavior. A common example a function returning a reference to an automatic variable:
std::string& f() { std::string str = "Example"; return str; // exits the scope of str: str's destructor is called } // stack storage deallocated ... std::string& r = f(); // r is a dangling reference std::cout << r; // Undefined: reads from a dangling reference std::string s = f(); // Undefined: uses a dangling reference to copy-initialize s
Note that rvalue references and lvalue references to const extend the lifetimes of temporary objects, see reference initialization for rules and exceptions.
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 for details.