C++20 新特性:constexpr 与 consteval 的区别与使用场景

在 C++20 之前,constexpr 用来指示一个表达式可以在编译期求值,编译器会在需要时尝试对其进行编译期求值;而在运行时如果编译器无法确定其值,仍然会以运行时方式执行。C++20 引入了 consteval,它完全要求在编译期求值,任何非编译期调用都会导致编译错误。下面从语义、使用场景以及两者的交互来深入探讨这两种限定符。

1. 基本语法与语义

constexpr int add(int a, int b) {
    return a + b;          // 该函数可以在编译期或运行期调用
}

consteval int mul(int a, int b) {
    return a * b;          // 必须在编译期调用
}
  • constexpr:可以在编译期求值,也可以在运行时求值。它的返回值类型必须是字面量类型或引用。
  • consteval:只能在编译期调用。若尝试在运行时调用,则编译器报错。返回值必须是字面量类型。

2. 何时使用 constexpr

  • 需要编译期求值,但也希望在运行时可用:例如一个通用的 max 函数,既能在模板元编程中计算,也能在普通运行时调用。
  • 与标准库中的 constexpr 函数兼容:如 std::sqrtstd::abs 等已被声明为 constexpr 的函数。
constexpr int factorial(int n) {
    return n <= 1 ? 1 : (n * factorial(n-1));
}

int main() {
    constexpr int fact5 = factorial(5);   // 编译期求值
    int arr[fact5];                       // 可用作数组大小
    std::cout << factorial(10) << '\n';   // 运行时求值
}

3. 何时使用 consteval

  • 强制编译期求值:当你想确保某个函数只能在编译期使用,以避免不必要的运行时开销或错误调用。
  • 实现编译期常量:例如在生成编译期常量表、验证参数等场景。
  • 配合 if constexprconsteval 函数可用于决定模板分支的编译期路径。
consteval int computeTableSize(int entries) {
    if (entries <= 0) {
        throw "entries must be positive";
    }
    return entries * 2;   // 仅在编译期可见
}

int main() {
    constexpr int tableSize = computeTableSize(256); // 编译期
    int table[tableSize];                           // 合法
    // int bad = computeTableSize(-5); // 编译错误
}

4. 两者的交互与注意事项

  1. consteval 函数不能返回非字面量类型。例如不能返回 std::string,因为它不是字面量类型。
  2. constexpr 函数可以返回非字面量类型,只要满足字面量构造。例如返回 std::array<int, N>
  3. consteval 函数可以调用 constexpr 函数,但反之不成立。
  4. 在模板元编程中consteval 可以配合 requires 子句,确保模板参数满足编译期约束。
template<int N>
requires consteval (N > 0)
struct ArrayHolder {
    int arr[N];
};

5. 性能与编译器支持

  • 大多数主流编译器(GCC 11+, Clang 13+, MSVC 19.32+)已完整实现 C++20 的 consteval
  • consteval 的强制编译期求值往往会让编译时间略有增长,但可显著降低运行时开销,尤其在需要大规模常量计算时更为明显。

6. 小结

特性 是否可运行时求值 是否必须编译期 适用场景
constexpr 需要兼容运行时调用,或在标准库中常见
consteval 强制编译期执行,确保无运行时开销,适合参数验证、编译期表计算等

实用建议:在实现库接口时,优先使用 constexpr,保持兼容性;当你需要完全的编译期保证,或想让错误更早被捕获时,再考虑使用 consteval。记住,consteval 是“如果你不想让它在运行时存在,那就用它”。

发表评论