绑定引用到对象
语法
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) | (C++11 起) |
给定 R fn ( T & arg ); 或 R fn ( T && arg ); fn ( object ) fn ( { arg1, arg2, … } ) | (3) | |
T & fn () 或 T && fn () 内return object ; | (4) | |
给定 Class 内的 T & ref ; 或 T && ref ; Class :: Class(…) : ref( object) {…} | (5) | |
解释
能以 T
类型的对象、T
类型的函数或可隐式转换到 T
的对象,初始化 T
的引用。一旦初始化,则不能更改引用使之引用另一对象。
在下列情形初始化引用:
1) 以初始化器声明具名左值引用变量时
2) 以初始化器声明具名右值引用变量时
3) 当函数形参拥有引用类型时,在函数调用表达式中
4) 当函数返回引用类型时,在 return 语句中
引用初始化的效果是:
- 若初始化器是花括号初始化器列表
{
arg1, arg2, …}
,则遵循列表初始化。
- 否则,若引用是左值引用:
- 若 object 是左值表达式,且其类型为
T
或派生于T
,而且有相等或更少的 cv 限定,则引用被绑定到左值表达式所标识的对象或其基类子对象。
- 若 object 是左值表达式,且其类型为
- double d = 2.0;
- double& rd = d; // rd 引用 d
- const double& rcd = d; // rcd 引用 d
- struct A {};
- struct B : A {} b;
- A& ra = b; // ra 引用 b 中的 A 子对象
- const A& rca = b; // rca 引用 b 中的 A 子对象
- 否则,若 object 的类型与
T
不相同且不从它派生,而 object 拥有到左值的转换函数,其类型为T
或派生于T
并有相等或更少的 cv 限定,则绑定引用到转换函数所返回的左值所标识的对象(或到其基类子对象)。
- 否则,若 object 的类型与
- struct A {};
- struct B : A { operator int&(); };
- int& ir = B(); // ir 指代 B::operator int& 的结果
- 否则,若引用是 const 的左值引用或右值引用 (C++11 起):
- 若 object 是非位域右值或函数左值,且其类型是
T
或派生于T
,拥有相等或更少 cv 限定,则绑定引用到初始化器表达式的值或其基类子对象(经实质化临时量之后,若需要) (C++17 起)。
- 若 object 是非位域右值或函数左值,且其类型是
- struct A {};
- struct B : A {};
- extern B f();
- const A& rca2 = f(); // 到 B 右值的 A 子对象。
- A&& rra = f(); // 同上
- int i2 = 42;
- int&& rri = static_cast<int&&>(i2); // 直接绑定到 i2
- 否则,若 object 的类型不同于
T
且不从它派生,而 object 拥有到右值或函数左值的转换函数,其类型为T
或从T
派生,且拥有有相等或更少的 cv 限定,则绑定引用到转换函数的结果或到其基类子对象(在实质化临时量后,若需要) (C++17 起)。
- 否则,若 object 的类型不同于
- struct A {};
- struct B : A {};
- struct X { operator B(); } x;
- const A& r = x; // 绑定到转换结果的 A 子对象
- B&& rrb = x; // 直接绑定到转换的结果
- 否则,将 object 隐式转换为
T
。绑定引用到转换(在实质化临时量后) (C++17 起)的结果。若 object(或若由用户定义转换函数进行转换,则为转换函数的结果)类型为T
或派生于T
,则它必须拥有等于或少于T
的 cv 限定,而若引用为右值引用,则它必须不是左值 (C++11 起)。
- 否则,将 object 隐式转换为
- struct A { operator volatile int&(); } a;
- struct B { operator int&(); } b;
- const int& r = a; // 错误:不能从转换结果直接初始化 r ,
- // 因为转换结果拥有类型 "volatile int&"
- int&& r2 = b; // 错误:转换结果拥有类型 "int&"
- const std::string& rs = "abc"; // rs 指代从字符数组复制初始化的临时量
- const double& rcd2 = 2; // rcd2 指代值为 2.0 的临时量
- int i3 = 2;
- double&& rrd3 = i3; // rrd3 指代值为 2.0 的临时量
临时量生存期
一旦引用被绑定到临时量或其子对象,临时量的生存期就被延续以匹配引用的生存期,但有下列例外:
- return 语句中绑定到函数返回值的临时量不被延续:它立即于返回表达式的末尾销毁。这种函数始终返回悬垂引用。
- 在构造函数初始化器列表中绑定到引用成员的临时量,只持续到构造函数退出前,而非对象存在期间(注意:这种初始化从 DR 1696 开始非良构)。 | (C++14 前) |
- 在函数调用中绑定到函数形参的临时量,存在到含这次函数调用的全表达式结尾为止:若函数返回一个引用,而其生命长于全表达式,则它将成为悬垂引用。
- 绑定到 new 表达式中所用的初始化器中的引用的临时量,存在到含该 new 表达式的全表达式结尾为止,而非被初始化对象的存在期间。若被初始化对象的声明长于全表达式,则其引用成员将成为悬垂引用。
- 绑定到用直接初始化语法(括号),而非列表初始化语法(花括号)初始化的聚合体的引用元素中的引用的临时量,存在直至含该初始化器的全表达式末尾为止。
| (C++20 起) |
总而言之,临时量的生存期不能以进一步“传递”来延续:从绑定了该临时量的引用初始化的第二引用不影响临时量的生存期。
注解
仅在函数形参声明,函数返回类型声明,类成员声明,以及带 extern
说明符时,引用可以不与初始化器一同出现。
示例
运行此代码
- #include <utility>
- #include <sstream>
- struct S {
- int mi;
- const std::pair<int,int>& mp; // 引用成员
- };
- void foo(int) {}
- struct A {};
- struct B : A {
- int n;
- operator int&() { return n; };
- };
- B bar() {return B(); }
- //int& bad_r; // 错误:无初始化器
- extern int& ext_r; // OK
- int main(){
- // 左值
- int n = 1;
- int& r1 = n; // 到对象 n 的左值引用
- const int& cr(n); // 引用可以有更多 cv 限定
- volatile int& cv{n}; // 可使用任何初始化器语法
- int& r2 = r1; // 另一到对象 n 的左值引用
- // int& bad = cr; // 错误:更少 cv 限定
- int& r3 = const_cast<int&>(cr); // 需要 const_cast
- void (&rf)(int) = foo; // 到函数的左值引用
- int ar[3];
- int (&ra)[3] = ar; // 到数组的左值引用
- B b;
- A& base_ref = b; // 到基类子对象的左值引用
- int& converted_ref = b; // 到转换结果的左值引用
- // 右值
- // int& bad = 1; // 错误:不能绑定左值引用到右值
- const int& cref = 1; // 绑定到右值
- int&& rref = 1; // 绑定到右值
- const A& cref2 = bar(); // 到 B 临时量的 A 子对象的引用
- A&& rref2 = bar(); // 相同
- int&& xref = static_cast<int&&>(n); // 直接绑定到 n
- // int&& copy_ref = n; // 错误:不能绑定到左值
- double&& copy_ref = n; // 绑定到值为 1.0 的右值临时量
- // 临时量生存期上的限制
- std::ostream& buf_ref = std::ostringstream() << 'a'; // ostringstream 临时量
- // 被绑定到 operator<< 的左运算数,但其生存期在分号结束,
- // 故 buf_ref 为悬垂引用。
- S a { 1, {2, 3} }; // 绑定临时量 pair {2,3} 到引用成员 a.mp 并延长其生存期以匹配 a
- S* p = new S{ 1, {2, 3} }; // 绑定临时量 pair {2,3} 到引用成员 p->mp ,
- // 但其生存期在分号结束
- // p->mp 是悬垂引用
- delete p;
- }
当前内容版权归 cppreference 或其关联方所有,如需对内容或内容相关联开源项目进行关注与资助,请访问 cppreference .