输入关键词开始搜索

C++ constexpr 与编译期计算

const vs constexpr

const int x = 42;          // 运行时常量(不可修改)
constexpr int y = 42;      // 编译期常量(一定在编译期已知)

int n;
const int a = n;           // ✅ 运行时确定的 const
constexpr int b = n;       // ❌ 编译期不可知

// 泛型编程中 constexpr 的价值:
constexpr int size = 10;
int arr[size];             // ✅ 编译期大小 → 栈上数组
array<int, size> stdArr;   // ✅ 模板参数必须是编译期常量

constexpr 函数

// C++11 限制:只能有一条 return 语句
constexpr int square11(int x) { return x * x; }

// C++14 放宽:可以有循环、分支、局部变量
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

// constexpr 函数也可在运行时调用
int n = 6;
int f6 = factorial(n);            // 运行时计算

if constexpr — C++17 编译期分支

// 传统做法:SFINAE 或标签分发,很繁琐
// C++17: 一行搞定

template <typename T>
auto getValue(T t) {
    if constexpr (is_pointer_v<T>)
        return *t;           // 编译期选这路,不需要对非指针类型合法
    else
        return t;
}

int x = 42;
getValue(x);     // int → return x
getValue(&x);    // int* → return *x

// 模板递归的终止条件
template <typename T, typename... Args>
void print(T first, Args... rest) {
    cout << first;
    if constexpr (sizeof...(rest) > 0) {
        cout << ", ";
        print(rest...);           // 只在有剩余参数时实例化
    }
}

consteval — C++20 即时函数

// constexpr: 可能编译期也可能运行期
// consteval: 必须在编译期执行

consteval int compileOnly(int n) {
    return n * n;
}

constexpr int x = compileOnly(5);  // ✅ 编译期

int n;
// int y = compileOnly(n);          // ❌ 编译错误:不能在运行期调用

// 使用场景:确保某个计算一定在编译期完成
consteval size_t strlen_ct(const char *s) {
    size_t n = 0;
    while (*s++) ++n;
    return n;
}

constexpr 容器(C++20)

// vector 和 string 可以在 constexpr 中使用!
constexpr int sumFirstN(int n) {
    vector<int> v;
    for (int i = 1; i <= n; ++i)
        v.push_back(i);
    int total = 0;
    for (int x : v) total += x;
    return total;
}
// ⚠️ constexpr 中 new 的内存必须在编译期内 delete

constexpr int s100 = sumFirstN(100);  // 5050,编译期计算

实战场景

场景 1:编译期字符串哈希

constexpr uint64_t hash(const char *s) {
    uint64_t h = 14695981039346656037ULL;
    while (*s) {
        h ^= static_cast<uint64_t>(*s++);
        h *= 1099511628211ULL;
    }
    return h;
}

switch (hash(str)) {  // 编译期哈希 → switch 直接用于字符串匹配
    case hash("start"): /*...*/ break;
    case hash("stop"):  /*...*/ break;
}

场景 2:编译期查表

// 编译期生成 sin 值表
template <size_t N>
constexpr array<double, N> sinTable() {
    array<double, N> table{};
    for (size_t i = 0; i < N; ++i)
        table[i] = sin(2 * M_PI * i / N);
    return table;
}

constexpr auto sinLUT = sinTable<360>();  // 360 个值在编译期算完
double fastSin(int deg) { return sinLUT[deg % 360]; }

场景 3:类型安全的单元量纲

template <int M, int K, int S>
struct Unit {
    double value;

    template <int M2, int K2, int S2>
    constexpr auto operator*(Unit<M2, K2, S2> other) const {
        return Unit<M + M2, K + K2, S + S2>{value * other.value};
    }
};

using Meter = Unit<1, 0, 0>;
using Second = Unit<0, 0, 1>;
using Speed = Unit<1, 0, -1>;   // m/s

constexpr Meter m{10};
constexpr Second s{2};
constexpr Speed v = m / s;  // 编译期计算:Unit<1,0,-1> 类型安全

速查:各版本能力

版本constexpr 能力
C++11变量、单 return 函数
C++14循环、分支、局部变量
C++17if constexpr、Lambda 可为 constexpr
C++20constevalconstinit、vector/string 在 constexpr 中
C++23if consteval、constexpr 虚函数