%%
# 纲要
> 主干纲要、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)