Document number: P4427R0
Audience: EWG


Ville Voutilainen
2026-10-23

Configurable contracts for virtual functions

Abstract

This paper supplies a design and implementation exploration for configurable contracts on virtual functions.

During the development of C++26 contracts, there was a long debate on whether contracts on virtual functions should follow a Design-by-Contract (DbC) model, in which an overriding function inherits the contract of the function it overrides and may only weaken its preconditions and strengthen its postconditions, or the model that is now adopted into the C++29 working draft (P3097).

Arguments and examples have been presented for why the DbC model is unsuitable for some users, and for why the other model is. Contracts on virtual functions were removed from C++26 over these concerns. P3097 brought them back into the C++29 working draft with the same model, without addressing the concerns that caused their removal. Neither model satisfies everybody.

This paper, and the prototype implementation backing it, takes a different approach. The P4324 model already makes aspects of contract checking configurable through control objects; this paper makes the inheritance of contracts configurable in the same way, and lets the control object decide. Users then aren't stuck with one model that some of them will find unsuitable. Had P3097 addressed the concerns that led to virtual-function contracts being removed from C++26, this paper wouldn't be needed. Instead, it accepts that some audiences have taken the P3097 model as far as the C++29 working draft, and tries to remedy the situation by making the inheritance semantics configurable through a control object.

Background: control objects

In P4324, every contract assertion has a control object: a constexpr object whose type answers compile-time queries about the assertion, such as whether it is ignored, and whose function call operator handles its evaluation and any violation. A contract names its control object in angle brackets, as in pre<ctrl>(x > 0). Every query is a static constexpr member function taking a std::contracts::assertion_static_info, and an absent query has a default.

The approach

Two mechanisms are proposed. Together, they can express the C++29-adopted model, wholesale inheritance of a base's contract, and the DbC model, and they can be chosen per contract.

1. Automatic inheritance: inherited()

A control object type may declare the query

static constexpr bool inherited(std::contracts::assertion_static_info);

If a virtual function's contract has a control object for which inherited() returns true, an overriding function that declares no contract of its own behaves as if it had declared that same contract, with the same control object. If the query is absent, it returns false: the override gets no contract, as in the C++29-adopted model. The details:

2. Explicit composition: base_contract<Base>()

An override's own contract can refer to the corresponding contract of a named base class with std::contracts::base_contract<Base>(), combined with ordinary operators. This is what expresses DbC subcontracting: a weaker precondition is base_contract<Base>() || extra, and a stronger postcondition is base_contract<Base>() && extra. The rules:

Expressing the models

Examples

The examples use a control object that reports violations and continues instead of terminating:

namespace sc = std::contracts;

template <bool Inherit>
struct report {
  const char* what;
  static constexpr bool is_ignored(sc::assertion_static_info) { return false; }
  static constexpr bool constify(sc::assertion_static_info) { return false; }
  static constexpr bool assumable(sc::assertion_static_info) { return false; }
  static constexpr bool inherited(sc::assertion_static_info) { return Inherit; }
  void operator()(const sc::assertion_context& ctx) const
  { if (!ctx.check()) std::cerr << "  violated: " << what << '\n'; }
};

Inherited and not inherited

inline constexpr report<false> not_inherited{"Shape::rotate's precondition"};
inline constexpr report<true>  inherited{"Shape::scale's precondition"};

struct Shape {
  virtual void scale(double f) pre<inherited>(f > 0) {}
  virtual void rotate(double deg) pre<not_inherited>(deg >= 0 && deg < 360) {}
  virtual ~Shape() = default;
};

struct Circle : Shape {
  void scale(double) override { }   // inherits f > 0
  void rotate(double) override { }  // inherits nothing
};

Circle c;
Shape& s = c;
c.scale(-1);   // violation: Circle::scale has inherited f > 0
c.rotate(-1);  // no check: Circle::rotate has no contract
s.rotate(-1);  // violation, checked by the caller: the call names Shape::rotate

Inherited on one side only

struct client_only {
  // is_ignored, constify, assumable and operator() as above
  static constexpr bool inherited(sc::assertion_static_info info)
  { return info.side() == sc::assertion_check_side::client; }
};
inline constexpr client_only client_only_v{};

struct Base {
  virtual int f(int x) pre<client_only_v>(x >= 0) { return x; }
  virtual ~Base() = default;
};
struct Derived : Base {
  int f(int x) override { return 2 * x; }
};

Derived d;
d.f(-1);   // one violation, reported by the caller; Derived::f itself checks nothing

Design by Contract

class Reader {
  int limit = 100;   // private: the base's contract can still use it
public:
  virtual int read(const int n) pre<pre_v>(n > 0 && n <= limit)
                                post<post_v>(r: r >= 0 && r <= n)
  { return n; }
  virtual ~Reader() = default;
};

struct EvenReader : Reader {
  int read(const int n) override
    pre<pre_v>(sc::base_contract<Reader>() || n == 0)            // weaker: also accepts 0
    post<post_v>(r: sc::base_contract<Reader>() && r % 2 == 0)  // stronger: r is also even
  { return n & ~1; }
};

EvenReader e;
e.read(0);     // OK: the weakened precondition accepts 0
e.read(1000);  // violation: neither the base's precondition nor n == 0 holds

Note that EvenReader's contract refers to Reader's, which uses the private member limit. That works because the base's contract is evaluated with the base's own access, as described below.

Implementation approach

The implementation never copies the base's contract condition into the derived class. A base's condition may use members private to the base, to which the derived class has no access, and shouldn't need any. Instead:

Only P4324 contracts, which always have a control object, are allowed on virtual functions in the implementation; plain C++26 contracts on virtual functions remain ill-formed.

Implementation experience

The design is implemented in a GCC branch: P4324 contracts on virtual functions, inherited(), and base_contract<Base>(), with tests covering one-side and both-side inheritance, an override's own contract taking precedence, disagreement within a set, ambiguity across bases, transitive inheritance, postconditions, covariant return types, private members, side reporting, and DbC weakening and strengthening chains across three levels.

The examples above are runnable on Compiler Explorer with the "x86-64 gcc (P4324 contracts, and profiles)" compiler, using -fcontract-control-objects:

When a contract is checked on both sides, the examples report its violation twice, once by the caller and once in the definition.

Wording

To be supplied.