Initialization of references (C++ only)
When you initialize a reference, you bind that reference to an object, which is not necessarily the object denoted by the initializer expression.
Once a reference has been initialized, it cannot be modified
to refer to another object. For example:
int num1 = 10;
int num2 = 20;
int &RefOne = num1; // valid
int &RefOne = num2; // error, two definitions of RefOne
RefOne = num2; // assign num2 to num1
int &RefTwo; // error, uninitialized reference
int &RefTwo = num2; // valid
Note that the initialization of a reference is not the same as an assignment to a reference. Initialization operates on the actual reference by binding the reference to the object it is an alias for. Assignment operates through the reference on the object referred to.
A reference can be declared without an initializer:
- When it is used in a parameter declaration
- In the declaration of a return type for a function call
- In the declaration of class member within its class declaration
- When the
extern
specifier is explicitly used
Reference binding
Suppose
T
and U
are
two types. If ignoring top-level cv-qualifiers, T
is
of the same type as U
or is a base class of U
, T
and U
are reference-related.Example 1
typedef int t1;
typedef const int t2;
In this example, t1
and t2
are
reference-related.If T
and U
are
reference-related, and T
is at least as cv-qualified
as U
, T
is reference-compatible with U
.
In Example 1, t1
is not reference-compatible with t2
,
but t2
is reference-compatible with t1
.
If
an lvalue reference
r
to type T
is
to be initialized by an expression e
of type U
,
and T
is reference-compatible with U
,
the reference r
can be bound directly to e
or
a base class subobject of e
unless T
is
an inaccessible or ambiguous base class of U
. Example 2
int a = 1;
const int& ra = a;
struct A {};
struct B: A {} b;
A& rb = b;
In this example, the const int
type
is reference-compatible with the int
type, so ra
can
be bound directly to a
. Structure A
is
reference-related to structure B
, so rb
can
be bound directly to b
.If an lvalue reference
r
to
type T
is to be initialized by an expression e
of
type U
, r
can be bound to the lvalue
result of the conversion of e
or a base class of e
if
the following conditions are satisfied. In this case, the conversion
function is chosen by overload resolution.U
is a class type.T
is not reference-related toU
.e
can be converted to an lvalue of typeS
, andT
is reference-compatible withS
.
Example 3
struct A {
operator int&();
};
const int& x= A();
In this example, structure A
is
a class type, and the const int
type is not reference-related
to structure A
. However, A
can be
converted to an lvalue of type int
, and const
int
is reference-compatible with int
, so
reference x
of type const int
can
be bound to the conversion result of A()
.By
default, the compiler cannot bind a non-const or volatile lvalue reference
to an rvalue.
Example 4
int& a = 2; // error
const int& b = 1; // ok
In this example, the variable a
is
a non-const lvalue reference. The compiler cannot bind a
to
the temporary initialized with the rvalue expression 2
,
and issues an error message. The variable b
is a
nonvolatile const lvalue reference, which can be initialized with
the temporary initialized with the rvalue expression 1
.If you specify the LANGLVL(COMPATRVALUEBINDING) option, the compiler can bind a
non-const or volatile lvalue reference to an rvalue of a user-defined type where an
initializer is not required. The default value of this option is LANGLVL(NOCOMPATRVALUEBINDING). This compiler behavior conflicts with the rvalue
references feature, which does not allow a non-const or volatile lvalue reference to be bound to an
rvalue. If both of the features are enabled, the compiler issues an error message.
Notes:
- A non-const or volatile lvalue reference cannot be bound to an rvalue of a built-in type.
- A non-const or volatile lvalue reference that is a class member cannot be bound to an rvalue.
Suppose an expression
e
of
type U
belongs to one of the following value categories:- An xvalue
- A class prvalue
- An array prvalue
- A function lvalue
r
to
type T
is to be initialized by the expression e
,
and T
is reference-compatible with U
,
reference r
can be initialized by expression e
and
bound directly to e
or a base class subobject of e
unless T
is
an inaccessible or ambiguous base class of U
. Example 5
int& func1();
int& (&&rf1)()=func1;
int&& func2();
int&& rf2 = func2();
struct A{
int arr[5];
};
int(&&ar_ref)[5] = A().arr;
A&& a_ref = A();
In this example, rf1
, rf2
, ar_ref
,
and a_ref
are all rvalue references. rf1
is
bound to the function lvalue func1
, rf2
is
bound to the xvalue result of the call func2()
, ar_ref
is
bound to the array prvalue A().arr
, and a_ref
is
bound to the class prvalue A()
.Suppose
r
is
an rvalue reference or nonvolatile const lvalue reference to type
T, and r
is to be initialized by an expression e
of
type U
. r
can be bound to the conversion
result of e
or a base class of e
if
the following conditions are satisfied. In this case, the conversion
function is chosen by overload resolution.U
is a class type.T
is not reference-related toU
.e
can be converted to an xvalue, class prvalue, or function lvalue type ofS
, andT
is reference-compatible withS
.
Example 6
int i;
struct A {
operator int&&() {
return static_cast<int&&>(i);
}
};
const int& x = A();
int main() {
assert(&x == &i);
}
In this example, structure A
is a
class type, and the const int
type is not reference-related
to structure A
. However, A
can be
converted to an xvalue of type int
, and const
int
is reference-compatible with int
, so
reference x
of const int
can be
initialized with A()
and bound to variable i
. An
rvalue reference can be initialized with an lvalue in the following
contexts:
- A function lvalue
- A temporary converted from an lvalue
- An rvalue result of a conversion function for an lvalue object that is of a class type
Example 7
int i = 1;
int&& a = 2; // ok
int&& b = i; // error
double&& c = i; // ok
In this example, the rvalue reference a
can
be bound to the temporary initialized with the rvalue expression 2
,
but the rvalue reference b
cannot be bound to the
lvalue expression i
. You can bind the rvalue reference c
to
the temporary value 1.0
that is converted from the
variable i
.