C++20 的 Constexpr:编译期计算的全能武器

在 C++20 之前,constexpr 已经为我们提供了编译期计算的能力,但在 C++20 中它被彻底提升,变成了真正可以在编译时执行几乎所有常规代码的强大工具。下面我们将从历史回顾、语义演变、典型应用、常见陷阱以及性能收益四个维度展开讨论。

1. 历史回顾

  • C++11constexpr 仅限于常量表达式;函数体只能是单个 return 语句,且参数必须是 constexpr
  • C++14:允许在 constexpr 函数中使用循环、递归、以及多语句;但仍不能包含局部非 constexpr 变量、异常处理等。
  • C++17:进一步放宽,支持更复杂的控制流、try-catch,并允许在类中使用 constexpr 成员。
  • C++20:彻底解放,几乎所有标准库容器都获得了 constexpr 支持;局部变量可为非 constexpr,但若满足条件可在编译期求值。

2. 语义演变

版本 关键限制 主要改进
C++11 只能单返回 支持单行返回
C++14 只能递归 支持循环、递归
C++17 不能异常 支持异常处理
C++20 不能使用非 constexpr 大多数 STL 支持 constexpr

核心概念:如果函数的所有输入都是 constexpr,且执行过程中没有违反 constexpr 的限制,编译器将尝试在编译期求值。 这意味着我们可以把任何可在编译时确定的计算逻辑写成普通函数,而不必担心手动写宏或模板元编程。

3. 典型应用

3.1 编译期矩阵乘法

#include <array>

template <std::size_t N>
using Matrix = std::array<std::array<int, N>, N>;

constexpr int multiply(int a, int b) { return a * b; }

template <std::size_t N>
constexpr Matrix <N> matmul(const Matrix<N>& A, const Matrix<N>& B) {
    Matrix <N> C{};
    for (std::size_t i = 0; i < N; ++i)
        for (std::size_t j = 0; j < N; ++j)
            for (std::size_t k = 0; k < N; ++k)
                C[i][j] += multiply(A[i][k], B[k][j]);
    return C;
}

在编译期生成 `Matrix

` 的乘积,得到一个全静态初始化的数据结构,避免了运行时的开销。 ### 3.2 常量表达式日志格式 “`cpp #include #include constexpr std::string_view format_log(const char* level, const char* msg) { if (level == nullptr || msg == nullptr) return “invalid”; std::string_view prefix = “[“; // placeholder // … 拼接逻辑 return “Formatted log”; // 伪代码 } “` 编译期格式化可以在模板元编程中生成错误消息,或者在 `static_assert` 中给出更友好的提示。 ### 3.3 递归 Fibonacci “`cpp constexpr unsigned long long fib(unsigned n) { return n

发表评论