C++ 参考手册
- C++11
- C++14
- C++17
- C++20
- C++ 编译器支持情况表
- 独立与宿主实现
- C++ 语言
- 变量模板(C++14 起)
- 整数字面量
- 聚合初始化
- 比较运算符
- 默认比较(C++20 起)
- 转义序列
- for 循环
- while 循环
- 用户定义转换
- SFINAE
- 主函数
- ASCII 码表
- 标识符
- 类型
- 内存模型
- 对象
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 类
- 运算符重载
- 模板
- 异常
- 事务性内存
- 占位符类型说明符 (C++11 起)
- decltype 说明符
- 函数声明
- final 说明符 (C++11 起)
- override 说明符(C++11 起)
- 引用声明
- 移动构造函数
- 移动赋值运算符
- 枚举声明
- constexpr 说明符(C++11 起)
- 列表初始化 (C++11 起)
- 构造函数与成员初始化器列表
- using 声明
- nullptr,指针字面量
- 基础类型
- 类型别名,别名模版 (C++11 起)
- 形参包
- 联合体声明
- 字符串字面量
- 用户定义字面量 (C++11 起)
- 属性说明符序列(C++11 起)
- Lambda 表达式 (C++11 起)
- noexcept 说明符 (C++11 起)
- noexcept 运算符 (C++11 起)
- alignof 运算符(C++11 起)
- alignas 说明符 (C++11 起)
- 存储类说明符
- 基于范围的 for 循环 (C++11 起)
- static_assert 声明
- 隐式转换
- 代用运算符表示
- 自增/自减运算符
- 折叠表达式(C++17 起)
- 类模板实参推导(C++17 起)
- 模板形参与模板实参
- if 语句
- inline 说明符
- 结构化绑定声明 (C++17 起)
- switch 语句
- 字符字面量
- 命名空间
- 求值顺序
- 复制消除
- consteval 说明符 (C++20 起)
- constinit 说明符 (C++20 起)
- 协程 (C++20)
- 模块 (C++20 起)
- 约束与概念 (C++20 起)
- new 表达式
- do-while 循环
- continue 语句
- break 语句
- goto 语句
- return 语句
- 动态异常说明
- throw 表达式
- try 块
- 命名空间别名
- 类声明
- cv(const 与 volatile)类型限定符
- 默认初始化
- 值初始化(C++03 起)
- 零初始化
- 复制初始化
- 直接初始化
- 常量初始化
- 引用初始化
- 值类别
- C++ 运算符优先级
- 布尔字面量
- 浮点字面量
- typedef 说明符
- 显式类型转换
- static_cast 转换
- dynamic_cast 转换
- const_cast 转换
- reinterpret_cast 转换
- delete 表达式
- 构造函数与成员初始化器列表
- this 指针
- 访问说明符
- 友元声明
- virtual 函数说明符
- explicit 说明符
- 静态成员
- 默认构造函数
- 复制构造函数
- 复制赋值运算符
- 析构函数
- 类模板
- 函数模板
- 显式(全)模板特化
- 汇编声明
- C++ 的历史
- 作用域
- 生存期
- 定义与单一定义规则(ODR)
- 名字查找
- 有限定的名字查找
- 无限定的名字查找
- 如同规则
- 未定义行为
- 翻译阶段
- 常量表达式
- 赋值运算符
- 算术运算符
- 逻辑运算符
- 成员访问运算符
- 其他运算符
- sizeof 运算符
- typeid 运算符
- 指针声明
- 数组声明
- 语言链接
- 详述类型说明符
- 默认实参
- 变长实参
- 实参依赖查找
- 重载决议
- 重载函数的地址
- 注入类名
- 非静态数据成员
- 非静态成员函数
- 嵌套类
- 派生类
- 空基类优化
- 抽象类
- 位域
- 转换构造函数
- 成员模板
- 模板实参推导
- 部分模板特化
- sizeof... 运算符
- 待决名
- 函数 try 块
- 扩充命名空间 std
- 字母缩写
- RAII
- 三/五/零之法则
- PImpl
- 零开销原则
- 类型
- 隐式转换
- 注释
- C++ 关键词
- 预处理器
- C++ 标准库头文件
- 具名要求
- 功能特性测试 (C++20)
- 工具库
- 类型支持(基本类型、RTTI、类型特性)
- 概念库 (C++20)
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 容器库
- 迭代器库
- 范围库 (C++20)
- 算法库
- 数值库
- 输入/输出库
- 文件系统库
- 本地化库
- 正则表达式库
- 原子操作库
- 线程支持库
- 实验性 C++ 特性
- 有用的资源
- 索引
- std 符号索引
- 协程支持 (C++20)
- C++ 关键词
引用初始化
绑定引用到对象
语法
T T T T |
(1) | ||||||||
T T T T |
(2) | (C++11 起) | |||||||
给定 R fn ( T & arg ); 或 R fn ( T && arg );
fn fn |
(3) | ||||||||
T & fn () 或 T && fn () 内
|
(4) | ||||||||
给定 Class 内的 T & ref ; 或 T && ref ;
Class |
(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 语句中绑定到函数返回值的临时量不被延续:它立即于返回表达式的末尾销毁。这种函数始终返回悬垂引用。
|
(C++14 前) |
- 在函数调用中绑定到函数形参的临时量,存在到含这次函数调用的全表达式结尾为止:若函数返回一个引用,而其生命长于全表达式,则它将成为悬垂引用。
- 绑定到 new 表达式中所用的初始化器中的引用的临时量,存在到含该 new 表达式的全表达式结尾为止,而非被初始化对象的存在期间。若被初始化对象的声明长于全表达式,则其引用成员将成为悬垂引用。
struct A { int&& r; }; A a1{7}; // OK:延续生存期 A a2(7); // 良构,但有悬垂引用 |
(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; }