引用初始化
将引用绑定到对象。
目录 |
[编辑] 语法
[编辑] 非列表初始化
T & ref = target ; T |
(1) | ||||||||
T && ref = target ; T |
(2) | (C++11 起) | |||||||
func-refpar ( target ) |
(3) | ||||||||
return target ; |
(4) | (在 func-refret 的定义内部) | |||||||
Class:: Class( ...) : ref-member ( target ) { ... } |
(5) | (在 Class 的定义内部) | |||||||
[编辑] 普通列表初始化 (C++11起)
T & ref = { arg1, arg2, ... }; T |
(1) | ||||||||
T && ref = { arg1, arg2, ... }; T |
(2) | ||||||||
func-refpar ({ arg1, arg2, ... }); |
(3) | ||||||||
[编辑] 指定列表初始化 (C++20起)
T & ref = {. des1 = arg1 , . des2 { arg2 } ... }; T |
(1) | ||||||||
T && ref = {. des1 = arg1 , . des2 { arg2 } ... }; T |
(2) | ||||||||
func-refpar ({. des1 = arg1 , . des2 { arg2 } ... }); |
(3) | ||||||||
对 T
的引用可以用 T
类型的对象、T
类型的函数或隐式转换为 T
的对象进行初始化。一旦初始化,引用不能被重新绑定(改变)以引用另一个对象。
引用在以下情况中初始化:
[编辑] 解释
T | - | 被引用类型 |
ref | - | 要初始化的引用变量 |
target | - | 正在使用的初始化表达式 |
func-refpar | - | 一个带有引用类型参数的函数(T & 或 T && (C++11起)) |
func-refret | - | 一个返回类型为引用类型(T & 或 T && (C++11起))的函数 |
Class | - | 类名 |
ref-member | - | Class 的引用类型(T & 或 T && (C++11起))的非静态数据成员 |
des1, des2, ... | - | 指定符 |
arg1, arg2, ... | - | 初始化列表中的初始化器 |
[编辑] 定义
对于两个类型 T1
和 T2
- 给定
T1
和T2
的 cv-不合格版本分别为U1
和U2
,如果U1
与U2
相似,或者U1
是U2
的基类,则T1
与T2
引用相关。 - 如果类型为“指向
T2
的指针”的纯右值可以通过标准转换序列转换为类型为“指向T1
的指针”,则T1
与T2
引用兼容。
[编辑] 初始化规则
如果引用初始化使用普通或指定(C++20起)列表初始化,则遵循列表初始化的规则。 |
(C++11 起) |
对于非列表引用初始化,给定 target 的类型为 U
,引用要么直接绑定到 target,要么绑定到从 target 转换而来的 T
类型的值。首先考虑直接绑定,然后是间接绑定;如果两者都不可用,则程序格式错误。
在所有使用两种类型的引用兼容关系来建立引用绑定有效性的情况中,如果标准转换序列会格式错误,那么需要这种绑定的程序就是格式错误的。
[编辑] 直接绑定
如果满足以下所有条件:
- 要初始化的引用是左值引用。
- target 是非位域左值。
-
T
与U
引用兼容。
则引用绑定到 target,或其适当的基类子对象。
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
否则,如果满足以下所有条件:
- 要初始化的引用是左值引用。
-
U
是类类型。 -
T
与U
不引用相关。 - target 可以转换为
V
类型的左值,使得T
与V
引用兼容。
则引用绑定到转换的左值结果,或其适当的基类子对象。
struct A {}; struct B : A { operator int&(); }; int& ir = B(); // ir refers to the result of B::operator int&
否则,如果待初始化的引用是左值引用,且 T
未被 const 限定或被 volatile 限定,则程序格式错误。
double& rd2 = 2.0; // error: not an lvalue and reference is not const int i = 2; double& rd3 = i; // error: type mismatch and reference is not const
否则,如果满足以下所有条件:
- target 是以下任何类别的对象:
|
(C++11 前) |
|
(C++11 起) (C++17 前) |
|
(C++17 起) |
-
T
与U
引用兼容。
则引用绑定到 target,或其适当的基类子对象。
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
如果 target 是一个纯右值,则对其应用临时实体化,将纯右值的类型视为调整后的类型
在这种情况下,引用绑定到结果对象,或其适当的基类子对象。 |
(C++17 起) |
否则,如果满足以下所有条件:
-
U
是类类型。 -
T
与U
不引用相关。 - target 可以转换为
V
类型的值 v,使得T
与V
引用兼容,其中 v 属于以下任何类别:
|
(C++11 前) |
|
(C++11 起) (C++17 前) |
|
(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
如果转换结果是纯右值,则对其应用临时实体化,将纯右值的类型视为调整后的类型
在这种情况下,引用绑定到结果对象,或其适当的基类子对象。 |
(C++17 起) |
[编辑] 间接绑定
如果直接绑定不可用,则考虑间接绑定。在这种情况下,T
不能与 U
引用相关。
如果 T
或 U
是类类型,则使用复制初始化 T
类型对象的规则考虑用户定义转换。如果相应的非引用复制初始化格式错误,则程序格式错误。然后,调用转换函数的结果(如非引用复制初始化所述)用于直接初始化引用。对于此直接初始化,不考虑用户定义转换。
否则,创建 |
(C++17 前) |
否则,target 被隐式转换为“cv-不合格 |
(C++17 起) |
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
[编辑] 临时对象的生命周期
每当引用绑定到临时对象或其子对象时,临时对象的生命周期将延长以匹配引用的生命周期(检查临时对象生命周期异常),其中临时对象或其子对象由以下表达式之一表示:
|
(C++17 前) |
(C++17 起) |
- 带括号的表达式 (e),其中 e 是这些表达式之一,
- 形式为 a[n] 或 n[a] 的内置下标表达式,其中 a 是一个数组并且是这些表达式之一,
- 形式为 e.m 的类成员访问表达式,其中 e 是这些表达式之一,并且 m 指示对象类型的非静态数据成员,
- 形式为 e.*mp 的指向成员的指针操作,其中 e 是这些表达式之一,并且 mp 是指向数据成员的指针,
- 不带用户定义转换的
const_cast
、static_cast
、dynamic_cast
或reinterpret_cast
转换,它将这些表达式之一转换为指代操作数所指定的对象或其完整对象或其子对象的泛左值(显式转换表达式被解释为这些转换的序列), - 形式为 cond ? e1 : e2 的条件表达式,它是一个泛左值,其中 e1 或 e2 是这些表达式之一,或
- 形式为 x, e 的内置逗号表达式,它是一个泛左值,其中 e 是这些表达式之一。
此生命周期规则有以下例外:
|
(直到 C++26) |
- 绑定到函数调用中引用参数的临时对象存在,直到包含该函数调用的完整表达式结束:如果函数返回的引用超出完整表达式的生命周期,它将成为一个悬空引用。
|
(C++11 起) |
struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference |
(C++20 起) |
通常,临时对象的生命周期不能通过“传递它”来进一步延长:从临时对象所绑定的引用变量或数据成员初始化的第二个引用不会影响其生命周期。
[编辑] 注意
引用仅在函数参数声明、函数返回类型声明、类成员声明以及带有extern
说明符时才不带初始化器出现。
在解决 CWG issue 1696 之前,允许临时对象绑定到构造函数初始化器列表中的引用成员,并且它仅在构造函数退出时才存在,而不是与对象一样长。自 CWG 1696 以来,这种初始化是格式错误的,尽管许多编译器仍然支持它(一个值得注意的例外是 clang)。
[编辑] 示例
#include <sstream> #include <utility> 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 // the 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 // the lifetime of object 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; // Imitate [[maybe_unused]] applied to the following variables: [](...){} ( cv, r2, r3, rf, ra, base_ref, converted_ref, a, cref, rref, cref2, rref2, copy_ref, xref ); }
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 391 | C++98 | 用类类型的 const 限定引用初始化 右值可能创建一个临时对象,并且该类的构造函数 需要将右值复制到该临时对象中。 |
没有临时对象 被创建,构造函数 不被要求 |
CWG 450 | C++98 | const 限定数组的引用不能用引用兼容的数组右值初始化。 用引用兼容的数组右值初始化。 |
允许 |
CWG 589 | C++98 | 引用不能直接绑定到数组或类右值 | 允许 |
CWG 656 | C++98 | 用非引用兼容但具有到引用兼容类型转换函数的类型初始化的 const 限定类型引用被绑定到从转换函数的返回值(或其基类子对象)复制的临时对象 引用兼容但具有转换函数到引用 兼容类型被绑定到从转换函数的返回值(或其基类子对象)复制的临时对象 值(或其基类子对象)的临时副本 |
直接绑定到返回值 值(或其基类 子对象)直接 |
CWG 1287 | C++11 | 从类类型 target 到另一个引用兼容类型的转换只能是隐式的 引用兼容类型只能是隐式的 |
允许显式 转换 |
CWG 1295 | C++11 | 引用可以绑定到位域亡值 | 已禁止 |
CWG 1299 | C++98 | 临时对象的定义不明确 | 已明确 |
CWG 1571 | C++98 | 间接绑定中的用户定义转换没有考虑 target 的类型 绑定中的用户定义转换没有考虑 target 的类型 |
已考虑 |
CWG 1604 | C++98 | 间接绑定中未考虑用户定义转换 | 已考虑 |
CWG 2352 | C++98 | 引用兼容性未考虑限定符转换 | 已考虑 |
CWG 2481 | C++17 | 间接绑定中临时实体化结果类型未添加 cv-限定符 间接绑定中临时实体化结果类型未添加 cv-限定符 |
已添加 |
CWG 2657 | C++17 | 间接绑定中临时实体化结果类型未添加 cv-限定符 直接绑定中临时实体化结果类型未添加 cv-限定符 |
已添加 |
CWG 2801 | C++98 | 允许间接绑定使用引用相关类型 | 已禁止 |