Namespaces
Variants
Views
Actions

Reference initialization

From cppreference.com
< cpp‎ | language

Binds a reference to an object

Contents

[edit] Syntax

T & ref = object ;

T & ref = { arg1, arg2, ... };

T & ref ( object ) ;

T & ref { arg1, arg2, ... } ;

(1)
T && ref = object ;

T && ref = { arg1, arg2, ... };

T && ref ( object ) ;

T && ref { arg1, arg2, ... } ;

(2) (since C++11)
given R fn ( T & arg ); or R fn ( T && arg );

fn ( object )

fn ( { arg1, arg2, ... } )

(3)
given T & fn () { or T && fn () {

return object ;

(4)
Class::Class(...) : refmember( expr) {...} (5)

[edit] Explanation

A reference to T can be initialized with an object of type T, a function of type T, or an object implicitly convertible to T. Once initialized, a reference cannot be changed to refer to another object.

References are initialized in the following situations:

1) When a named lvalue reference variable is declared with an initializer
2) When a named rvalue reference variable is declared with an initializer
3) In a function call expression, when the function parameter has reference type
4) In the return statement, when the function returns a reference type
5) When a non-static data member of reference type is initialized using a member initializer

The effects of reference initialization are:

  • If the initializer is a braced-init-list { arg1, arg2, ... } , rules of list initialization are followed.
  • Otherwise, if the reference is an lvalue reference:
  • If object is an lvalue expression, and its type is T or derived from T, and is equally or less cv-qualified, then the reference is bound to the object identified by the lvalue or to its base class subobject.
double d = 2.0;
double& rd = d;        // rd refers to d
const double& rcd = d; // rcd refers to d
struct A {};
struct B : A {} b;
A& ra = b;             // ra refers to A subobject in b
const A& rca = b;      // rca refers to A subobject in b
  • Otherwise, if the type of object is not same or derived from T, and object has conversion function to an lvalue whose type is either T or derived from T, equally or less cv-qualified, then the reference is bound to the object identified by the lvalue returned by the conversion function (or to its base class subobject).
struct A {};
struct B : A { operator int&(); };
int& ir = B(); // ir refers to the result of B::operator int&
  • Otherwise, if the reference is lvalue reference to const or rvalue reference (since C++11):
  • If object is a non-bit-field rvalue or a function lvalue, and its type is either T or derived from T, equally or less cv-qualified, then the reference is bound to the value of the initializer expression or to its base subobject (after materializing a temporary if necessary) (since C++17).
struct A {};
struct B : A {};
extern B f();
const A& rca2 = f(); // bound to the A subobject of the B rvalue.
A&& rra = f();       // same as above
 
int i2 = 42;
int&& rri = static_cast<int&&>(i2); // bound directly to i2
  • Otherwise, if the type of object is not same or derived from T, and object has conversion function to a rvalue or a function lvalue whose type is either T or derived from T, equally or less cv-qualified, then the reference is bound to the result of the conversion function or to its base class subobject (after materializing a temporary if necessary) (since C++17).
struct A {};
struct B : A {};
struct X { operator B(); } x;
const A& r = x; // bound to the A subobject of the result of the conversion
B&& rrb = x;    // bound directly to the result of the conversion
  • Otherwise, object is implicitly converted to T. The reference is bound to the result of the conversion (after materializing a temporary) (since C++17). If the object (or, if the conversion is done by user-defined conversion, the result of the conversion function) is of type T or derived from T, it must be equally or less cv-qualified than T, and, if the reference is an rvalue reference, must not be an lvalue (since C++11).
const std::string& rs = "abc"; // rs refers to temporary copy-initialized from char array
const double& rcd2 = 2;        // rcd2 refers to temporary with value 2.0
int i3 = 2;
double&& rrd3 = i3;            // rrd3 refers to temporary with value 2.0

[edit] Lifetime of a temporary

Whenever a reference is bound to a temporary or to a subobject thereof, the lifetime of the temporary is extended to match the lifetime of the reference, with the following exceptions:

  • a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of the return expression. Such function always returns a dangling reference.
  • a temporary bound to a reference member in a constructor initializer list persists only until the constructor exits, not as long as the object exists. (note: such initialization is ill-formed as of DR 1696).
(until C++14)
  • a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if the function returns a reference, which outlives the full expression, it becomes a dangling reference.
  • a temporary bound to a reference in the initializer used in a new-expression exists until the end of the full expression containing that new-expression, not as long as the initialized object. If the initialized object outlives the full expression, its reference member becomes a dangling reference.

In general, the lifetime of a temporary cannot be further extended by "passing it on": a second reference, initialized from the reference to which the temporary was bound, does not affect its lifetime.

[edit] Notes

References appear without initializers only in function parameter declaration, in function return type declaration, in the declaration of a class member, and with the extern specifier.

[edit] Example

#include <utility>
#include <sstream>
 
struct S {
    int mi;
    const std::pair<int, int>& mp; // reference member
};
 
void foo(int) {}
 
struct A {};
 
struct B : A {
    int n;
    operator int&() { return n; }
};
 
B bar() { return B(); }
 
//int& bad_r;      // error: no initializer
extern int& ext_r; // OK
 
int main() {
//  Lvalues
    int n = 1;
    int& r1 = n;                    // lvalue reference to the object n
    const int& cr(n);               // reference can be more cv-qualified
    volatile int& cv{n};            // any initializer syntax can be used
    int& r2 = r1;                   // another lvalue reference to the object n
//  int& bad = cr;                  // error: less cv-qualified
    int& r3 = const_cast<int&>(cr); // const_cast is needed
 
    void (&rf)(int) = foo; // lvalue reference to function
    int ar[3];
    int (&ra)[3] = ar;     // lvalue reference to array
 
    B b;
    A& base_ref = b;        // reference to base subobject
    int& converted_ref = b; // reference to the result of a conversion
 
//  Rvalues
//  int& bad = 1;        // error: cannot bind lvalue ref to rvalue
    const int& cref = 1; // bound to rvalue
    int&& rref = 1;      // bound to rvalue
 
    const A& cref2 = bar(); // reference to A subobject of B temporary
    A&& rref2 = bar();      // same
 
    int&& xref = static_cast<int&&>(n); // bind directly to n
//  int&& copy_ref = n;                 // error: can't bind to an lvalue
    double&& copy_ref = n;              // bind to an rvalue temporary with value 1.0
 
//  Restrictions on temporary lifetimes
    std::ostream& buf_ref = std::ostringstream() << 'a'; // the ostringstream temporary
                      // was bound to the left operand of operator<<
                      // but its lifetime ended at the semicolon
                      // so buf_ref is a dangling reference
 
    S a {1, {2, 3} };         // temporary pair {2, 3} bound to the reference member
                              // a.mp and its lifetime is extended to match a
    S* p = new S{1, {2, 3} }; // temporary pair {2, 3} bound to the reference
                              // member p->mp, but its lifetime ended at the semicolon
                              // p->mp is a dangling reference
    delete p;
}


[edit] See also