constexpr
函数是如何在编译期执行的?constexpr
函数局部变量以及循环是如何实现?constexpr if
是什么原理?或许你有不少疑惑,但目前关于constexpr
编译期的资料还比较少,本文笔者将从 gcc 的源码简单分析一下,以此来抛砖引玉,让更多的人加入到对constexpr
的讨论中。
我用的是最新的gcc 11源码,直接翻到gcc/gcc/cp/constexpr.cc
,在这个文件里我们可以看到gcc是如何解析constexpr
的。
我们先介绍几个重要的数据结构
tree
是gcc内部的语法树。
constexpr_call
是gcc内部对constexpr函数的包装,它将函数定义、函数参数以及函数结果打包在一起,以便进行编译期求值。可将其和标准库的std::function
类比一下,具体结构如下
struct constexpr_call {
/* constexpr函数定义 */
constexpr_fundef *fundef;
/* 打包的constexpr函数参数 */
tree bindings;
/* 函数调用结果
1. NULL表示constexpr函数正在被求值
2. error_mark_node表示求值出现错误
3. 其他值则表示正确的调用结果 */
tree result;
...
};
constexpr_global_ctx
是全局constexpr上下文,每个cxx_eval_outermost_constant_expr(),即最外层的constexpr调用都需要有一个全局constexpr上下文,保存着表达式内部变量到到初始化值的映射,具体结构如下
struct constexpr_global_ctx {
/* 保存常量表达式内部的局部变量或临时变量 */
hash_map<tree,tree> values;
/* 在计算最外层常量表达式时创建的堆 */
auto_vec<tree, 16> heap_vars;
/* 待释放的堆数量 */
unsigned heap_dealloc_count;
...
};
constexpr_ctx
是constexpr上下文,具体结构如下
struct constexpr_ctx {
/* 当前的全局constexpr上下文 */
constexpr_global_ctx *global;
/* 正在求值的constexpr函数 */
constexpr_call *call;
/* 保存当前循环的表达式,如果没有在循环中则为 NULL */
vec<tree> *save_exprs;
/* 当前constexpr对象的构造函数 */
tree ctor;
/* 正在构造的constexpr对象. */
tree object;
...
};
用于编译期constexpr调用栈的上下文
// 调用栈
static vec