学习定位:现代 C++ 核心知识。C++20 路线中先掌握“编译期常量、constexpr 函数、if constexpr”即可;consteval、复杂编译期计算和模板元编程可后续按需补。

1. 是什么

constexpr 声明可在编译期求值的变量或函数。它让计算从运行期前置到编译期,消除运行时开销,并允许将结果用于编译期上下文(数组大小、模板实参、static_assert 等)。

2. 比 const 强在哪

  • const 只保证不可修改,值可以在运行期确定。
  • constexpr 要求编译期就要知道值,所以能用于 const 无法胜任的场合。
int n = std::rand();
const int a = n;             // ✅ 运行期常量
constexpr int b = n;         // ❌ 编译错误:n 不是常量表达式
 
constexpr int max = 100;
int arr[max];                // ✅ 编译期常量用作数组大小

3. 何时用 const vs constexpr

场景使用
值在运行期才能确定(用户输入、rand() 等)const
只需保证对象不被修改,不要求编译期可用const
值在编译期就能确定(字面量、编译期计算)constexpr
需要用于数组大小、模板参数、static_assertconstexpr
函数希望能在编译期执行以提升性能constexpr 函数

4. C++17 核心特性与示例

4.1 constexpr 变量

constexpr int answer = 42;
constexpr double pi = 3.14159;
constexpr const char* msg = "hello";

4.2 constexpr 函数(C++14 起已支持循环/分支,C++17 延续)

constexpr int factorial(int n) {
    int result = 1;
    for (int i = 2; i <= n; ++i)
        result *= i;
    return result;
}
constexpr int f5 = factorial(5);  // 编译期得 120

4.3 if constexpr(C++17 新增)

编译期条件分支,被丢弃的分支不会实例化,允许写出“按类型分派”的模板代码。

template<typename T>
auto to_string(T value) {
    if constexpr (std::is_arithmetic_v<T>) {
        return std::to_string(value);    // 数值类型走这里
    } else if constexpr (std::is_same_v<T, std::string>) {
        return value;                    // 字符串直接返回
    } else {
        static_assert(sizeof(T) == 0, "Unsupported type");
    }
}

4.4 constexpr 构造函数与类

struct Point {
    int x, y;
    constexpr Point(int x_, int y_) : x(x_), y(y_) {}
    constexpr int norm() const { return x * x + y * y; }
};
 
constexpr Point p(3, 4);
constexpr int dist = p.norm();  // 25

5. 误用 constexpr 示例

int get_value() { return 10; }        // 非 constexpr 函数
constexpr int v = get_value();        // ❌ 编译错误
 
int n;
std::cin >> n;
constexpr int s = n * 2;              // ❌ 编译错误:n 不是常量

6. 总结

  • 需要编译期确定性constexpr
  • 仅需运行期只读const
  • C++17 的 if constexpr 让泛型代码更简洁,消除无效分支实例化。
  • constexpr 不是万能钥匙,强行用在运行期值上会直接编译失败。