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++ 关键词
翻译阶段
编译器处理 C++ 源文件时,如同严格按照以下顺序进行各个阶段的处理:
阶段 1
1) 以实现定义方式,将源文件的各个单独字节,映射为基本源字符集的字符。特别是,依赖于 OS 的行尾指示符均被替换为换行字符。基本源字符集由以下 96 个字符组成:
a) 5 个空白字符(空格 (space)、水平制表 (horizontal tab)、垂直制表 (vertical tab)、换页 (form feed)和 换行 (new-line))
b) 10 个数字字符,从 '0' 到 '9'
c) 52 个字母,从 'a' 到 'z' 以及从 'A' 到 'Z'
d) 29 个标点字符:_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ’
3) 将各个三标符序列替换为其对应的单字符表示。
|
(C++17 前) |
阶段 2
1) 凡在反斜杠出现于行尾(其后紧跟换行符)时,删除反斜杠和换行符,把两个物理源码行组合成一个逻辑源码行。这是单趟操作:以两个反斜杠结束,后随一个空行的行,不会把三行组合为一。若于此阶段组成了通用字符名(\uXXXX),则行为未定义。
2) 若此步骤后,非空源文件不以换行符结束(无论是原本就无换行,还是以反斜杠结束),则其行为未定义 (C++11 前)添加一个终止的换行符 (C++11 起)。
阶段 3
1) 将源文件分解为注释,空白字符(空格、水平制表、换行、垂直制表和换页)的序列,和下列各种预处理记号 (preprocessing token):
a) 头文件名,如 <iostream> 或 "myfile.h"
b) 标识符
c) 预处理数字
f) 不属于任何其他类别的单独非空白字符
2) 恢复在阶段 1 和 2 期间,在任何原始字符串字面量的首尾双引号之间进行的所有变换。
|
(C++11 起) |
3) 以一个空格字符替换每段注释。
保持换行符。是否可将非换行空白字符序列缩减成单个空格字符是未指明的。
将输入分析为预处理记号,到某个给定字符为止时,通常将可构成一个预处理记号的最长字符序列处理成下一个预处理记号,即使这会导致后继分析失败也是如此。这常被称为最大吞噬 (maximal munch)。
int foo = 1; int bar = 0xE+foo; // 错误:非法的预处理数字 0xE+foo int baz = 0xE + foo; // OK int quux = bar+++++baz; // 错误:bar++ ++ +baz,而非 bar++ + ++baz。
最大吞噬规则的唯一例外是:
#define R "x" const char* s = R"y"; // 非良构的原始字符串字面量,而非 "x" "y" const char* s2 = R"(a)" "b)"; // 原始字符串字面量后随普通字符串字面量
struct Foo { static const int v = 1; }; std::vector<::Foo> x; // OK,<: 未被当作 [ 的代用记号 extern int y<::>; // OK,同 extern int y[]。 int z<:::Foo::value:>; // OK,int z[::Foo::value]; |
(C++11 起) |
- 头文件名预处理记号仅在
#include
指令中形成。
std::vector<int> x; // OK,<int> 不是头文件名
阶段 4
1) 执行预处理器。
2) #include 指令所引入的每个文件都经历阶段 1 到 4 的处理,递归执行。
3) 此阶段结束时,从源码移除所有预处理器指令。
阶段 5
2) 将字符字面量和非原始字符串字面量中的转义序列和通用字符名展开,并转换到执行字符集。
若某个通用字符名所指定的字符不是执行字符集的成员,则结果是由实现定义的,但保证不是空(宽)字符。
注意:某些实现中,能以命令行选项控制此阶段所进行的转换:gcc 和 clang 用 -finput-charset 指定源字符集的编码,用 -fexec-charset 和 -fwide-exec-charset 指定无编码前缀的 (C11 起)字符串和字符字面量中的执行字符集的编码,而 Visual Studio 2015 Update 2 及之后版本分别用 /source-charset 和 /execution-charset 指定源字符集和执行字符集。
阶段 6
拼接相邻的字符串字面量。
阶段 7
发生编译:将各个预处理记号转换成记号(token)。将所有记号当作一个翻译单元进行语法和语义分析并进行翻译。
阶段 8
检验每个翻译单元,产生所要求的模板实例化的列表,其中包含显式实例化所要求者。定位模板定义,并进行所要求的实例化,以产生实例化单元(instantiation unit)。
阶段 9
将翻译单元、实例化单元和为满足外部引用所需的库组件汇集成一个程序映像,它含有在其执行环境中执行所需的信息。
注意
某些编译器不实现实例化单元(又称为模板仓库或模板注册表),而是简单地在阶段 7 编译每个模板实例化,存储代码于其所显式或隐式要求的对象文件中,然后由连接器于阶段 9 将这些编译后的实例化缩减到一个。
引用
- C++11 standard (ISO/IEC 14882:2011):
- 2.2 Phases of translation [lex.phases]
- C++98 standard (ISO/IEC 14882:1998):
- 2.1 Phases of translation [lex.phases]