Skip to content

C++26 契约 (Contracts)

概述

契约(Contracts)是 C++26 的语言级代码正确性基础设施,允许在函数接口和逻辑中声明前置条件、后置条件和断言,具有标准化违规处理和可配置构建模式。

提案状态: P2900R14 已被 C++26 接受。

语法

前置条件 (pre)

cpp
int safe_divide(int a, int b)
    pre (b != 0)
    pre (a >= 0)
{ return a / b; }

后置条件 (post)

cpp
int absolute_value(int x)
    post (r: r >= 0)          // r 绑定返回值
{ return x < 0 ? -x : x; }

断言 (contract_assert)

cpp
void process(std::vector<int>& data) pre (!data.empty()) {
    std::ranges::sort(data);
    contract_assert(std::ranges::is_sorted(data));
}

违规处理

cpp
void my_handler(std::contract_violation const& v) {
    std::cerr << "契约违规 at " << v.source_location().line() << "\n";
    if (v.is_terminating()) std::abort();
}
std::set_contract_violation_handler(my_handler);

构建模式

模式行为场景
default实现定义一般开发
ignore不求值,零开销发布构建
observe求值并调用处理器,不终止日志/监控
bash
g++ -fcontract-mode=observe main.cpp

与 assert/static_assert 对比

特性assertstatic_assert契约
求值时机运行时编译时运行时(可配置)
发布构建被移除始终存在可选
位置函数体内任意声明处函数接口 + 函数体
违规处理abort编译错误自定义处理器
cpp
template <typename T>
T clamp(T val, T lo, T hi) {
    static_assert(std::is_arithmetic_v<T>);   // 编译期
    assert(lo <= hi);                          // 仅 Debug
    contract_assert(lo <= hi);                // 可配置
    return val < lo ? lo : (val > hi ? hi : val);
}

虚函数契约

cpp
class Shape {
public:
    virtual double area() const post (r: r >= 0.0);
};

class Circle : public Shape {
    double radius_;
public:
    Circle(double r) : radius_(r) pre (r > 0.0) {}
    double area() const override post (r: r > 0.0)
    { return 3.14159265358979 * radius_ * radius_; }
};

规则:派生类前置条件不可更强(只可放宽),后置条件不可更弱(只可收紧),确保 Liskov 替换原则。

完整示例

cpp
#include <vector>
#include <contract>

class Stack {
    std::vector<int> data_;
public:
    void push(int val) post: !data_.empty()
    { data_.push_back(val); }

    int pop() pre (!data_.empty()) {
        int val = data_.back();
        data_.pop_back();
        return val;
    }

    bool empty() const { return data_.empty(); }
};

int main() {
    Stack s;
    s.push(42);
    contract_assert(!s.empty());
    int val = s.pop();
    contract_assert(val == 42);
}

实现状态

编译器状态
GCC开发中,部分语法支持
Clang实验性分支
MSVC尚未公开实现

总结

C++26 契约将正确性检查从宏和注释提升为语言特性。通过标准化前置/后置条件、可自定义违规处理器和三种构建模式,契约在开发阶段提供保护,在发布阶段以零或可控开销运行。

基于 MIT 许可发布