引用初始化
来自cppreference.com
绑定引用到一个对象
目录 |
[编辑] 语法
T & ref = object ;
T T T |
(1) | ||||||||
T && ref = object ;
T T T |
(2) | (C++11 起) | |||||||
给定 R fn ( T & arg ); 或 R fn ( T && arg );
fn fn |
(3) | ||||||||
给定 T & fn () { 或 T && fn () {
|
(4) | ||||||||
Class:: Class(...) : refmember( expr) {...}
|
(5) | ||||||||
[编辑] 解释
到 T
的引用能以 T
类型对象、 T
类型函数或可隐式转换到 T
的对象初始化。一旦初始化,则引用不能被更改到引用另一对象。
引用在下列情形初始化:
1) 具名左值引用变量以初始化器声明时
2) 具名右值引用变量以初始化器声明时
3) 函数调用表达式中,函数参数拥有引用类型时
4) return 语句总,函数返回引用类型时
5) 引用类型的{{rlp|data members|非静态数据成员}用成员初始化器初始化是
引用初始化的效果是:
- 若初始化器是花括号初始化器列表
{
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
或从T
导出的类型,并有相等或更少的 cv 限定,则重载决议考虑源类型及其基类的返回左值引用的非 explicit 转换函数,并选择最佳者。引用绑定到转换函数所返回的左值所标识的对象(或其基类子对象)
- 若 object 是左值表达式,且其类型可隐式转换到
struct A { }; struct B : A { operator int&(); }; int& ir = B(); // ir 引用 B::operator int& 的结果
- 否则,若引用是右值引用或到 const 的左值引用:
- 若 object 是非位域亡值、类纯右值、数组纯右值 (C++17 前)右值 (C++17 起)或函数左值,且其类型是
T
或从T
导出,拥有相等或更少 cv 限定,则引用被绑定到初始化器表达式的值或其基类子对象(在临时量实质化后,若需要) (C++17 起)。
- 若 object 是非位域亡值、类纯右值、数组纯右值 (C++17 前)右值 (C++17 起)或函数左值,且其类型是
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
且不同于自它导出者 (C++17 起),且能隐式转换到类型T
或从T
导出者,有相等或更少 cv 限定的亡值、类纯右值 (C++17 前)右值 (C++17 起)或函数值,则引用被绑定到转换结果或其基类子对象(在临时量实质化后,若需要) (C++17 起)。不考虑用户定义转换 (C++17 起)
- 若 object 是类类型表达式之不同于
struct A { }; struct B : A { }; struct X { operator B(); } x; const A& r = x; // 绑定到转换结果的 A 子对象 B&& rrb = x; // 直接绑定到转换的结果
const std::string& rs = "abc"; // rs 引用从字符数组复制初始化的临时量 const double& rcd2 = 2; // rcd2 引用带值 2.0 的临时量 int i3 = 2; double&& rrd3 = i3; // rrd3 引用带值 2.0 的临时量
[编辑] 临时量生存期
无论引用被绑定到临时量还是临时量的基类子对象,临时量的生存期都被延续以匹配引用的生存期,除了下列例外:
- return 语句中绑定到函数返回值的临时量不被续命:它被立即于函数尾销毁。这种函数始终返回悬垂引用。
|
(C++14 前) |
- 在函数调用中绑定到函数参数的临时量,存在到含该函数调用的完整表达式结尾为止:若函数返回一个引用,而其生命长于完整表达式,则它成为悬垂引用。
- 绑定到用于 new 表达式的初始化器中的引用的临时量,存在到含该 new 表达式的完整表达式结尾为止,而非被初始化对象的存在期间。若被初始化对象的声明长于完整表达式,则其引用成员成为悬垂引用。
总而言之,临时量的生存期不能以进一步“传递”延续:从绑定了该临时量的引用初始化的第二引用不影响临时量的生存期。
[编辑] 注意
引用仅在函数参数声明、函数返回类型声明、类成员声明及带 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 // (注意: C++17 中无法编译) S* p = new S{ 1, {2,3} }; // 临时量 pair {2,3} 被绑定到引用成员 p->mp , // 但其生存期在分号结束 // p->mp 是悬垂引用 delete p; }