%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** %% # `constexpr` 说明符 C++11 引入的 **`constexpr` 关键字**,用以**显式==标识一个"表达式"为常量表达式**==[^1],并**指示==编译器进行检查验证==**——**如果被标识的表达式实际上并非常量表达式,无法在编译时求值,则将报编译错误**。 > [!caution] `constexpr` 应用于函数时,并不保证编译时求值 > > - 对于**constexpr 变量**,编译器要求**必须能够在编译时求值,否则会报错**。 > - 对于**constexpr 函数**,该关键字只是指示 **"函数==有可能==在编译时求值**。但**是否在编译时求值还==取决于函数是如何被调用的==**: > - 如果**调用上下文提供的是==编译时常量==**(即实参编译时可知),**那么求值就会在==编译时==进行**; > - 如果**提供的是运行时变量,那么求值就会在运行时进行**。 > [!summary] `constexpr` 作用总结 > > `constexpr` 可用于变量声明或函数声明: > > - `constexpr` 用于 **==变量声明==** 中则隐式地表示 `const` > - `constexpr` 用于 **==函数声明==** 中则隐式地表示 **`inline`** > - `constexpr` 用于 **==静态数据成员==** 中则隐式地表示 **`inline`** (**since C++17**) > - `constexpr` 用于 "**字面值常量类**" 的**构造函数**,从而**在编译时创建和初始化字面值常量类类型的对象**。 > ```cpp // 声明常量对象 constexpr int max_size = 100; // 该对象为"编译时常量"; 必须在编译时初始化,且其值在程序的整个生命周期中都是固定的。 constexpr int arraySize = 10; int arr[arraySize]; // 声明常量表达式函数 // 在C++11中,其函数体中只能包含单一的"return"语句,而不能有其它语句; constexpr int square(int x) { return x * x; } constexpr int x = square(10); // 在编译时就计算求值. ``` <br><br> ## constexpr 变量 被 `constexpr` 修饰的变量==**具有 `const` 属性**==(等同于被声明为 `const`), 表示该变量是一个**编译时常量**,**==将在编译时对其求值并初始化==**,其值在程序整个生命周期中不变。 > [!NOTE] `constexpr` 用于变量时,表明其不仅是常量,并且是 "**编译期可知的**" > > `constexpr` 变量保证在 "**==编译期求值==**",而 `const` 变量并**不一定**能在编译时求值,只保证是 "**运行时常量**"。 > > 因此,`constexpr` 变量均可可用作 "**模版非类型参数**",而 `const` 变量不一定: > > ```cpp > const int x = 66; // 字面量, 编译期求值. > const int y = getValue(); // 用另一个变量初始化, 或是用一个运行时求值的函数复制, 则是运行时常量. > > constexpr int z = 4; // 编译期求值 > > std::array<int, x> arr_x; // 正确 > // std::array<int, y> arr_y // 错误! 常量y不能在编译时求值 > std::array<int, z> arr_z; // 正确 > ``` > > <br> **`constexpr` 声明的变量**必须同时满足以下要求: - 变量类型必须是 **==字面量类型(LiteralType)==**,包括**基本数据类型、指针类型、枚举类型**。 - 必须在定义时被**显式初始化**; - 其**初始化项的完整表达式**(包括所有隐式转换、构造函数调用等),必须是 **==常量表达式==**。 - ~~自 C++20 起,其它的要求:略~~ > [!NOTE] `vector` 等 STL 容器不是字面量类型,因此不可以声明为 `constexpr`! <br> ##### constexpr 用于指针或引用 `constexpr` 也可用于**指针**或**引用类型**,等同于具有 "**==const==**" 属性(**对指针而言是顶层 const**,**对引用则是底层 const**)。 `constexpr` 用于**指针**时,该限定符**只对指针有效**,而与指针所指对象无关。 **一个 `constexpr` 指针或引用的==初始值必须是 `nullptr` 或 `0`,或是存储于某个固定地址中的对象==**。 ```cpp const int *p = nullptr; // 指向整型常量的指针 constexpr int *p = nullptr; // 指向整型的"常量指针"(const expr) ``` <br><br><br> ## constexpr 函数 > 自 C++11 引入,在 C++14 以及 17 之后的作用都有所变化。 > - C++11 中 `constexpr` 函数体内**仅限于具有一条返回语句**,函数的返回类型及形参类型都得是/**字面值类型**。 > - C++14 允许 `constexpr` 函数体内有更多的控制流语句; > - C++20 进一步增强了 `constexpr` 的能力,使得**可以在编译时执行更复杂的计算**。 `constexpr` 用于指示 **该函数 "==满足==" 在编译时进行计算的条件**,但**是否在编译时计算==取决于调用上下文**==: - 如果函数调用上下文提供了一组 **==编译时常量==** 作为参数,则**将在编译时就计算返回一个常量值**; - 如果提供的**函数实参不是常量表达式**,而是运行时变量,则**允许运行时求值**。 > [!info] `constexpr` 函数**具有 `inline` 性质**,为**内联函数**。 #### 注意事项 如果一个**函数或函数模板**的**任一声明中带有 `constexpr` 说明符**,则其 **==所有声明都必须包含该说明符==**。 ```cpp constexpr int square(int x) { return x * x; } constexpr int squaredValue = square(4); // 编译时求值 int y = 5; int squaredRuntime = square(y); // 运行时求值,因为y在编译时不是常量 ``` **`constexpr` 函数可用于任何需要常量表达式的地方,例如初始化 `constexpr` 变量,而普通函数不行**。 ```cpp constexpr int sz = size(); // 仅当`size()`是一个`constexpr`函数时才能正确编译 ``` <br><br> ## `constexpr` 构造函数 **`constexpr` 函数**、构造器、析构器所需满足的要求参见 [cppreference: constexpr specifier](https://en.cppreference.com/w/cpp/language/constexpr#constexpr) <br><br> # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later # ♾️参考资料 # Footnotes [^1]: [constexpr specifier (since C++11) - cppreference.com](https://en.cppreference.com/w/cpp/language/constexpr)