const 表示运行期不要改这个对象:初始化之后不能再赋值。constexpr 更进一步:值或函数结果可以在编译期就算出来,用来当数组长度、模板参数。constexpr 函数在实参是运行期变量时仍可在运行期调用;C++20 的 consteval 则强制只能在编译期调用。本节先写出可运行的 const 与 square,再对照三者。指针上的 const 位置见指针。

const:只读

形参写成 const std::string&,既避免拷贝,又禁止函数里改调用方的字符串。指向常量的指针 const int* 不能通过指针改所指对象;对象自己若不是 const,仍可通过原变量改。

C++ const 指针运行
#include <iostream>

int main() {
    int value = 10;
    const int* q = &value;  // 不能写 *q = 20
    value = 20;             // 通过原变量改仍然可以
    std::cout << *q << "\n";
    return 0;
}

输出:

20

类的 const 数据成员只能在构造函数的初始化列表里赋值,函数体内不能再改,见构造函数。

constexpr:编译期可求值

constexpr int area = square(10) 在编译期就算好。square(side) 里 side 是普通变量,同一函数改在运行期算,仍然合法。

C++ constexpr 函数运行
#include <iostream>

constexpr int square(int x) {
    return x * x;
}

int main() {
    constexpr int area = square(10);
    int side = 5;
    std::cout << area << " " << square(side) << "\n";
    return 0;
}

输出:

100 25

不能把运行期才知道的量赋给 constexpr 变量,例如用 cin 读进来的数去初始化 constexpr int n,无法通过编译。

怎么选

  • 只要不可变,用 const。上限可以来自配置文件,运行期初始化即可。
  • 要用在数组长度、static_assert、模板非类型参数上,用 constexpr。
  • constexpr 已经包含只读含义,再写一层 const 合法但多余。

了解:consteval 与 static_assert

consteval 函数必须在编译期调用:compile_only(7) 可以,compile_only(x) 而 x 是普通变量则非法。static_assert(square(10) == 100) 在编译期检查条件,失败则直接报错。深度用法可在掌握模板后再展开。

本节说明了只读修饰与编译期求值的分工。下一节介绍auto 与 decltype:由编译器推导类型时的规则与常见陷阱。