constexpr 是 C++11 引入的关键字,表示一个表达式或函数可以在 编译期求值

constexpr 修饰变量

编译期计算值。constexpr 本身不是类型限定符,但它对变量 隐含顶层 const

举例说明:

1
constexpr int* ptr = &val;
  • ptr 本身不可变(不能指向别的地址)。
  • 但可以通过 ptr 修改 val 的值(*ptr = 10; 是合法的)。

constexpr 修饰函数

告诉编译器:

“如果参数都是编译期常量,编译的时候就把结果算出来;如果不是,就把它当作普通函数在运行期执行。”


为什么要使用 constexpr?

把运行时的计算提前到了编译期,减少了运行时开销


如何实现编译时计算?

这是 constexpr 的核心,也是一直困扰我的问题。

我困扰的点是:AST 解释执行 这个东西除了教学以外还有什么应用场景?

之前在写 Pinky 编译器 的时候,第一个实现的执行方式就是遍历 AST 执行,但我一直很难想到这种执行方式的其他实际应用场景。今天突然想到 constexpr,来看了一下它的实现,终于明白了它的使用场景。

constexpr 的实现原理

正如上面所说,constexpr 实现编译时计算的方式就是 遍历 AST 解释执行

  • 遍历顺序:求值顺序驱动的遍历。
    • 大部分是 后序遍历
    • 也有特殊情况:如赋值语句(先右后左)、逻辑与/或(短路求值)等。
  • 执行宿主:具体执行计算的宿主语言当然就是 C++。
  • 结果处理:执行结果直接填入目标位置。

实例:GCC 中的 AST 表示

4 + 5 为例,这个 PLUS_EXPR 在 GCC 的 AST 树中的表示如下:

AST 详细树结构

这个属于 binop 中的 plus,遍历规则是 后序遍历

  1. 先分别拿到 PLUS_EXPR 的左右子树的值(4 和 5)。
  2. 然后用宿主语言 C++ 计算结果(9)。
  3. 结果直接替换到 AST 中或填入目标位置。

后续计划

后续会写一些编译器 / GC 相关的博客,例如:

  • 不同编译器生成 AST 的方式(递归下降、优先级爬升)
  • 不同类型语言(编译型、纯解释型、解释 + JIT 型)的 GC 实现差异