文章

条款15:尽可能使用constexpr

constexpr 可在编译期求值,提升性能与安全性;用于函数、变量、构造函数都有效,推荐优先使用。

条款15:尽可能使用constexpr

条款15:尽可能使用 constexpr

constexpr 表示一个值 不仅是常量(const而且必须在编译期已知(compile-time constant)

constexpr 对象 vs 函数

用法描述
constexpr 变量必须在编译期能被计算,且隐含 const
constexpr 函数如果传入编译期常量,结果也会是编译期常量;否则在运行期计算。

constexpr 对象

  • 所有 constexpr 对象都是 const
  • 但并非所有 const 对象都是 constexpr(因为 const 对象可以运行期初始化)。

适用场景:

  • 数组长度、模板参数、枚举值、对齐修饰符等 需要整型常量表达式(ICE) 的地方。
1
2
3
4
5
constexpr int size = 10;
std::array<int, size> arr;  // ✅ 正确,size 是编译期常量

const int runtimeSize = getValue();  // ❌ 非 constexpr
std::array<int, runtimeSize> arr;   // ❌ 编译错误

constexpr 函数

  • 如果实参是编译期可知的常量 → 在编译期执行
  • 如果实参不是编译期常量 → 像普通函数一样在运行时执行
1
2
3
constexpr int pow(int base, int exp) noexcept {
    return (exp == 0 ? 1 : base * pow(base, exp - 1));
}

C++11 vs C++14 中的 constexpr 限制

C++11 限制C++14 改进
只能有一条 return 语句允许多语句、变量定义、循环等
成员函数隐式 const可以有非常量成员函数(如 setX
返回类型必须是字面值类型(Literal)更灵活(支持 void 返回等)

C++14 中更自然的写法:

1
2
3
4
5
constexpr int pow(int base, int exp) noexcept {
    int result = 1;
    for (int i = 0; i < exp; ++i) result *= base;
    return result;
}

constexpr 类的构造和成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
class Point {
public:
    constexpr Point(double x = 0, double y = 0) noexcept : x(x), y(y) {}
    constexpr double xValue() const noexcept { return x; }
    constexpr double yValue() const noexcept { return y; }
	
    // 可以有非常量成员函数
    constexpr void setX(double newX) noexcept { x = newX; }  // C++14+
    constexpr void setY(double newY) noexcept { y = newY; }  // C++14+

private:
    double x, y;
};

可以在 constexpr 函数中构造对象、访问成员、进行计算:

1
2
3
4
constexpr Point midpoint(const Point& p1, const Point& p2) noexcept {
    return Point{ (p1.xValue() + p2.xValue()) / 2,
                  (p1.yValue() + p2.yValue()) / 2 };
}

constexpr 的用途示例

  • 编译期计算表达式(替代浮点 std::pow)
  • 实例化模板参数
  • 初始化静态数组长度
  • 更高效:常量放入只读内存

注意事项

优点

  • constexpr 对象和函数 使用场景更多
  • 编译器可 更早发现错误,还能进行 性能优化(如内联、常量传播等)。

风险

  • constexpr接口的一部分
  • 一旦加入就不能轻易移除,否则会破坏调用代码。
  • 添加调试语句(如 I/O)或异常处理可能使函数不再 constexpr

设计建议

  • 如果函数能 始终在编译期求值,就声明为 constexpr
  • 不要为了 constexpr 而强行写死函数逻辑。
  • 对于长期稳定性有要求的接口,谨慎添加 constexpr

总结

建议原因
尽可能使用 constexpr提高函数和变量的适用范围,提升性能与可靠性
优先用于编译期必须常量的位置如模板参数、数组大小、alignas、枚举值等
C++14 更适合 constexpr 编程支持更丰富的函数体和返回类型
不盲目添加一旦添加,影响接口兼容性
本文由作者按照 CC BY 4.0 进行授权