C++ 的 92 个关键字——你认识几个?

从 int 到 co_await,一张地图看完所有保留字

通识重叠

◎学完本节你会

本节定位:这是阶段一的收官查阅页,不是新知识。建议先学完第 1~7 章再来, 那时你会发现"原来我早就用过其中 30 多个了"。本页所有代码与输出均在 g++ 15.2 / -std=c++20 下真实编译运行核对过。

1先动手:这个词能当变量名吗?

输入任意一个词,看它是关键字(绝对不能用)、有含义的标识符(能用但别用)、 还是普通名字(随便用)。下面的一排词点一下就能试。

在上方输入一个词,或点一个标签试试。

2冷知识:and、or、not 也是关键字

C++ 标准给 11 个运算符准备了"英文别名",它们是关键字(不能当变量名), 但日常几乎没人用——因为键盘上 && 比 and 好打。选一个看它等价于什么。

替代写法(合法但少见)

常规写法(等价)

为什么必须知道它们存在:读别人(尤其是欧洲程序员)的代码时会碰到; 更重要的是——你不能拿它们当变量名。int or = 1; 是编译错误, 很多人第一次遇到会以为是编译器抽风。

392 个关键字分成 12 组 + 替代写法

点组名展开/收起。每组给一句话定位 + 代表代码 + 真实编译输出。 右侧标记告诉你这组在哪个阶段才深入学:阶段一 阶段二 阶段四。

4三个最常搞错的"假关键字"

误区 1:override 和 final 是关键字?—— 不是。 它们是"有含义的标识符",只在虚函数声明末尾才有特殊作用,别处可以当普通变量名:
int override = 10; // ✅ 合法(但千万别这么写) int final = 20; // ✅ 合法
本机 g++ 15.2 实测:这两行都能编译通过。但一旦你写了 int override, 后面就没法再用 override 标注虚函数了——所以这是"能编译,但毁掉后续代码"的写法。
误区 2:import / module / await 是关键字?—— 目前不是。 本机 g++ 15.2 / -std=c++20 逐词实测:这三个词仍可用作变量名。 它们是 C++20 模块系统和协程的预留词,将来可能升级为关键字。transaction_safe 同理,从未进正式标准。
误区 3:const_cast 能改任何 const?—— 不能,改了就是 UB。
const int cx = 42; int& rx = const_cast<int&>(cx); rx = 99; // ❌ 未定义行为 cout << cx; // 实测仍打印 42,不是 99!
编译器把 cx 常量折叠了,rx=99 改的内存和 cout 读的根本不是一回事。 能编译、能跑、不报错,但结果是错的——这类坑最阴。 const_cast 只能去掉"本来就不是 const 的对象"上的 const 限定。

5速查总表:92 个关键字全在这里

按分组筛选,或点"全部"。鼠标悬停看它属于哪一组。这张表可以当字典用。

!易错点清单

① 把关键字当变量名:int class、int new、int or 全部编译错误。 命名前用上面的检测器扫一眼,或干脆避开所有 92 个词。
② 混淆 const 家族:const=运行时不可改;constexpr=编译期可算(可递归); consteval=强制只能编译期调用,传运行时变量直接编译错误;constinit=要求编译期初始化。
③ 不知道替代写法是关键字:and/or/not/bitand/xor/compl 等 11 个词都是保留字, 不能当标识符,哪怕你从来不用它们。
④ 以为 override/final/import/module 是关键字:实测都不是,能当变量名(但不该)。 反过来,把它们当"普通词"随便用在别处,也会让代码可读性崩掉。

✎练习题

Q1. 以下哪个不是 C++ 关键字?
override/final 是"有含义的标识符",只在虚函数声明末尾有特殊含义,可以当普通变量名(int override = 10; 实测合法)。而 nullptr(C++11)、constexpr(C++11)、thread_local(C++11) 都是真关键字。
Q2. and、or、not 在 C++ 中等价于什么?
A 项的 &/|/~ 对应的替代写法是 bitand/bitor/compl;C 项的 &=/|=/!= 对应 and_eq/or_eq/not_eq。依据 ISO/IEC 14882:2020 §2.5 Alternative tokens。
Q3. 以下哪个关键字的存储期语义已被废弃(C++17 起从标准移除)?
register 在 C++11 起语义被废弃、C++17 正式从标准移除(只保留为兼容占位符)。goto 从未被废弃(虽然不推荐用);volatile 在 C++20 中部分用法被废弃,但关键字本身仍是关键字。

Q4. 阅读"常量与修饰符"组的代码,写出 factorial(5) 和 square(7) 的输出值。 并说明:constexpr 函数和 consteval 函数有什么本质区别?

答案:factorial(5) = 120、square(7) = 49(本机实测)。
区别:constexpr 函数可以在编译期求值,但如果传的是运行时变量,它也能在运行时正常调用(两用); consteval 函数只能在编译期调用,传运行时变量会直接编译错误("立即函数")。 另外 constexpr 允许递归,consteval 也允许,但都要求编译期能算完。

Q5. 阅读"类与面向对象"组的 union 代码:先 d.i = 42,再 d.f = 3.14f, 之后打印 d.i 为什么变成了 1078523331?

答案:union 的所有成员共享同一块内存,后写入的 3.14f 覆盖了原来的 42。
3.14f 的 IEEE 754 单精度位模式是 0x4048F5C3,转成十进制正好是 1078523331(本机实测一致)。
关键理解:不是"值变了",而是同一段比特被换了种方式解读—— 按 int 读是 1078523331,按 float 读是 3.14。这也是 2.3 节"计算机里数是怎么存的"的延续。

Q6. 把下面 10 个关键字按本节的 12 组分类,并各写一句它的核心作用:

mutable co_yield bitand dynamic_cast explicit requires thread_local noexcept wchar_t default
关键字所属组核心作用
mutable4 · 常量与修饰符让成员即使在 const 成员函数里也能被修改(常用于缓存计数)
co_yield12 · 协程协程产出一个值并挂起,下次恢复时从这里继续
bitand替代写法等价于 &(按位与)
dynamic_cast11 · 类型转换运行时安全向下转型,失败返回 nullptr 而不是崩
explicit7 · 类与 OOP禁止构造函数的隐式转换,防止 Student s = "abc" 这种意外
requires8 · 模板给模板参数加约束,不满足时编译期就拦下(报错只有一行)
thread_local5 · 存储类每个线程各有一份独立副本,互不干扰
noexcept6 · 函数承诺该函数不抛异常,编译器可据此优化
wchar_t1 · 基本类型宽字符类型,Windows 上 2 字节(本机实测)
default2 · 分支switch 的兜底分支;也用于 = default 生成默认函数
难点提示:这 10 个词分属 8 个不同组,其中 mutable 和 thread_local 都涉及"存储期与可变性",是最容易混的一对——前者管"const 方法里能不能改",后者管"每个线程各一份"。

◎跨学科小贴士:为什么每种语言都有"保留字"

编译器读你的代码时,第一步叫词法分析——把一串字符切成一个个"词"(token)。 切完之后它必须回答一个问题:这个词是"指令"还是"名字"?

看到 int 它必须知道"这是要声明一个整数",而不是"这是个叫 int 的变量"。 唯一的办法就是事先约定一批词永远不能被用作名字——这就是保留字(关键字)。

这和自然语言里的词性是同一回事:中文里"因为""所以"是连词,你不会拿"所以"当人名; 英语里 if、while 是控制词,C++ 把它们锁死。语言要能被无歧义地解析,就必须有一部分词是"专用的"。

有趣的是,C++ 还留了一手:像 override 这种词不锁死,只在特定语法位置生效, 这样既能加新特性、又不会让十几年前的老代码突然编译失败——这叫向后兼容, 和工程里"加接口不改老接口"是同一个思想。阶段五的项目 A(自制一门小语言)你会亲手写这个"切词"的过程。