Reference declaration

From cppreference.com
< cpp‎ | language

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)
1) Lvalue reference declarator: the declaration S & D; declares D as an lvalue reference to the type determined by decl-specifier-seq S.
2) Rvalue reference declarator: the declaration S && D; declares 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 T&& is used in a function template, forms the rules that make std::forward possible)

(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 references

Rvalue 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)

#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.

#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.