Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C++  Module 18 – C++26 previews: reflection, contracts and safety

C++26 contracts: pre, post and contract_assert

Write C++26 preconditions, postconditions and contract_assert checks, choose how a build evaluates them and replace the violation handler, run on GCC 16.

  • Advanced
  • 20 minutes
  • Examples run with Clang 21.0.0, GCC 16.2.0 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 8 of 20 minutes free
Free: Three kinds of contract assertion check a call at three points · The build decides what a violation does · Practise: contracts and semantics. With a pass: 2 in-depth parts and 3 interview answers, about 12 minutes.

What you will learn

  • Write preconditions, postconditions and contract assertions
  • Choose an evaluation semantic when building a program
  • Install a custom contract-violation handler
  • Predict what each evaluation semantic does when a contract is violated

Before you start

In C++23, percent(5, 4) quietly returns 125, a percentage above 100, unless an assert that release builds remove happens to catch it. Contracts let a C++26 function state its assumptions in its declaration. A precondition, pre(…), must hold when the function is called; a postcondition, post(r : …), must hold for the result r when it returns; and the statement contract_assert(…) checks a condition inside a body, like assert but without the macro. When a check fails, a contract violation occurs, and its consequence is chosen when the program is built, not in the code: the checks can be ignored, observed (reported, then execution goes on) or enforced (reported, then the program stops). With GCC 16 contracts need only -std=c++26; Apple clang 21 and upstream Clang do not implement them yet.

Preview: GCC 16 only

Status: in the final C++26 draft. GCC 16.2 compiled every contract example with -std=c++26 alone; on macOS, linking GCC’s default violation handler also takes -lstdc++exp. Apple clang 21 does not accept contracts.

Three kinds of contract assertion check a call at three points

Follow one call of percent from top to bottom:

Preconditions run on entry, contract_assert in the body, postconditions on return; the build’s semantic decides what a violation does.The caller writespercent(5, 4)Inside the callThe result returnsto the callerOn a violation, thesemantic of the build:ignore: never checkedobserve: handler, then the call goes onenforce: handler, then the program stopsquick_enforce: stops, no handler1. Preconditions, on entrypre(whole > 0)pre(part >= 0 && part <= whole)2. The body runs;each contract_assertis checked whereit is written3. Postconditions,on a normal returnpost(r : r >= 0 && r <= 100)not checked when anexception leaveswhen a checkfails

When each kind of contract assertion is checked, and what a violation does

Text description of the diagram

The diagram follows one call from top to bottom.

  1. The caller writes percent(5, 4).
  2. Inside the call, first the preconditions are checked on entry: pre(whole > 0) and pre(part >= 0 && part <= whole).
  3. Then the body runs, and each contract_assert is checked where it is written.
  4. Last, the postconditions are checked on a normal return, here post(r : r >= 0 && r <= 100). They are not checked when an exception leaves the function.
  5. The result returns to the caller.

A dashed arrow labelled "when a check fails" leads from the box "Inside the call" to a double-bordered box at the bottom: the evaluation semantic chosen when building decides what happens. With ignore the checks never run; with observe the violation handler runs and then the call goes on; with enforce the handler runs and then the program stops; with quick_enforce the program stops without calling the handler.

Preconditions and postconditions are written after the function’s parameter list, before its body, and a function may have as many of each as it needs; they are checked in the order written. A postcondition can name the result, here r, and refers to the object the function returns. It is checked only when the function returns normally, never when an exception leaves it. contract_assert is a statement and can stand wherever a statement can, which makes it the replacement for assert inside a body, with one difference that matters: it does not depend on whether a macro such as NDEBUG happens to be defined.

Because preconditions and postconditions sit in the declaration, they are part of the function’s interface: they appear in the header, a reader sees them next to the parameter list, and tools can read them. GCC checks them inside the called function by default, and its -fcontracts-client-check=pre (or =all) option adds the checks at call sites as well.

All three check conditions that a correct program never violates. A contract is not input validation: if bad input can reach a function in normal use, such as a quantity typed by a user, check it with ordinary code and report an error. A contract says that the caller, or this function’s own code, has a bug if the condition fails.

Checkpoint

Check: Pre, post and contract_assert

  1. Question 1 of 2 A function with post(r : r > 0) throws an exception instead of returning. Is the postcondition checked?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: No; postconditions are checked only when the function returns normally

    A postcondition describes the result of a normal return. When an exception leaves the function there is no result, so nothing is checked.

  2. Question 2 of 2 A checkout form sends a quantity typed by the user. Where should "quantity is at least 1" be checked?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: In ordinary code that reports an error to the user; a contract would treat a normal input as a bug

    Contracts state what a correct program never violates. A user can type 0 in normal use, so that is input to validate and answer with a message, not a bug. Further inside, a function that only ever receives validated quantities can state pre(quantity >= 1).

The build decides what a violation does

percent.cpp C++ · percent.cpp
// Two preconditions, a postcondition and an assertion on one small function.
#include <cstdio>
#include <print>

int percent(const int part, const int whole)
    pre(whole > 0)
    pre(part >= 0 && part <= whole)
    post(r : r >= 0 && r <= 100)
{
    const int result = part * 100 / whole;
    contract_assert(result * whole <= part * 100);  // integer division rounds down, never up
    return result;
}

int main() {
    std::setvbuf(stdout, nullptr, _IONBF, 0);  // unbuffered, so output and violation messages appear in order
    std::println("percent(3, 4) = {}", percent(3, 4));
    std::println("percent(5, 4) = {}", percent(5, 4));  // part > whole breaks the second precondition
    std::println("done");
}
Built once per semantic, then without the flag Python · semantics_session.py
from session import run

# One program, built with each of GCC 16's four evaluation semantics, then once without the flag. On macOS, GCC 16.2
# keeps its default violation handler in libstdc++exp, so the builds that call a handler link it with -lstdc++exp.
run("""
g++-16 -std=c++26 -fcontract-evaluation-semantic=ignore percent.cpp -o percent
./percent
g++-16 -std=c++26 -fcontract-evaluation-semantic=observe percent.cpp -o percent -lstdc++exp
./percent
g++-16 -std=c++26 -fcontract-evaluation-semantic=enforce percent.cpp -o percent -lstdc++exp
./percent
g++-16 -std=c++26 -fcontract-evaluation-semantic=quick_enforce percent.cpp -o percent
./percent
g++-16 -std=c++26 percent.cpp -o percent -lstdc++exp
./percent
""")

Output

$ g++-16 -std=c++26 -fcontract-evaluation-semantic=ignore percent.cpp -o percent
$ ./percent
percent(3, 4) = 75
percent(5, 4) = 125
done
$ g++-16 -std=c++26 -fcontract-evaluation-semantic=observe percent.cpp -o percent -lstdc++exp
$ ./percent
percent(3, 4) = 75
contract violation in function int percent(int, int) at percent.cpp:7: part >= 0 && part <= whole
[assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no]
contract violation in function int percent(int, int) at percent.cpp:8: r >= 0 && r <= 100
[assertion_kind: post, semantic: observe, mode: predicate_false, terminating: no]
percent(5, 4) = 125
done
$ g++-16 -std=c++26 -fcontract-evaluation-semantic=enforce percent.cpp -o percent -lstdc++exp
$ ./percent
percent(3, 4) = 75
contract violation in function int percent(int, int) at percent.cpp:7: part >= 0 && part <= whole
[assertion_kind: pre, semantic: enforce, mode: predicate_false, terminating: yes]
terminate called without an active exception
[stopped by signal SIGABRT]
$ g++-16 -std=c++26 -fcontract-evaluation-semantic=quick_enforce percent.cpp -o percent
$ ./percent
percent(3, 4) = 75
terminate called without an active exception
[stopped by signal SIGABRT]
$ g++-16 -std=c++26 percent.cpp -o percent -lstdc++exp
$ ./percent
percent(3, 4) = 75
contract violation in function int percent(int, int) at percent.cpp:7: part >= 0 && part <= whole
[assertion_kind: pre, semantic: enforce, mode: predicate_false, terminating: yes]
terminate called without an active exception
[stopped by signal SIGABRT]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 semantics_session.py

GCC 16 chooses the evaluation semantic with -fcontract-evaluation-semantic=, and enforce is the default when the flag is missing, as the final draft recommends: the last build sets no semantic, and its report says semantic: enforce. The first four runs differ only in that flag:

  • ignore: the checks never run, and the program goes on as if no contract were written.
  • observe: the checks run; on a violation the handler is called, and the program goes on when it returns.
  • enforce: the checks run; on a violation the handler is called, and the program stops when it returns.
  • quick_enforce: the checks run; on a violation the program stops at once, without calling the handler.

Step through the observe build, then what the other three builds did:

Interactive · message by message percent(5, 4) under observe, one hand-over at a time Step through the observe build of percent.cpp. Each arrow is a call or a return between main, percent and the violation handler.
percent(5, 4) under observe, one hand-over at a time, step 9 of 9. Messages so far: main to percent(part, whole): percent(3, 4); percent(part, whole) to main: 75; main to percent(part, whole): percent(5, 4); percent(part, whole) to violation handler: violation: pre; violation handler to percent(part, whole): returns; percent(part, whole) to violation handler: violation: post; percent(part, whole) to main: 125.mainprinted 125 and donepercent(part, whole)stops at line 7violation handlernot calledpercent(3, 4)75percent(5, 4)violation: prereturnsviolation: post125

The drawing shows the whole scene; its 9 steps are listed below.

Every step as text
  1. percent(3, 4). The build used -fcontract-evaluation-semantic=observe. main calls percent(3, 4): both preconditions hold, the body computes 75, the contract_assert holds and so does the postcondition.
  2. Returns 75. Every check passed, so 75 comes back and main prints percent(3, 4) = 75. A correct call pays for the checks and nothing more.
  3. percent(5, 4). The second call breaks the second precondition: 5 is more than 4. That is a contract violation, reported at line 7 of percent.cpp, the line of the precondition, not of the call.
  4. The handler reports it. Under observe the check calls the violation handler. GCC's default handler prints two lines: the function, the file and line and the condition, then the kind (pre), the semantic and terminating: no.
  5. The call goes on. The handler returns, and under observe execution continues. The body computes 5 * 100 / 4 = 125, and the contract_assert, 125 * 4 <= 5 * 100, holds.
  6. The postcondition fails. post(r : r >= 0 && r <= 100) is checked on the normal return and fails for 125, so the handler is called a second time, for line 8.
  7. 125 reaches main. The handler returns again, percent returns 125, and main prints percent(5, 4) = 125 and done. Observing told you about the bug without preventing its effect.
  8. Under enforce. Built with enforce instead, the run is the same up to the first report. When the handler returns, the program stops through std::terminate, with SIGABRT, before 125 exists.
  9. quick_enforce and ignore. quick_enforce stops at the same check without calling the handler, so no report is printed. ignore runs no check at all, and percent(5, 4) quietly returns 125.

With ignore, percent(5, 4) quietly returns 125, a percentage above 100. With observe, GCC’s default handler reports the broken precondition, the call goes on, and the postcondition r <= 100 is reported as broken too, before 125 comes back: observing tells you about bugs without preventing their effects. With enforce, the first violation stops the program, here through std::terminate: The function C++ calls when exception handling cannot go on, for example when no handler matches a throw or an exception tries to leave a noexcept function. By default it calls std::abort, which ends the program with SIGABRT. More in “C++ exceptions: throw, catch and stack unwinding”, before the bad value exists. With quick_enforce, the program stops at the same point but skips the handler and its message, which keeps the checks small in the compiled code. The default handler’s two lines name the function, the file and line, the condition, the kind of assertion and the semantic. The line is the line of the precondition, not the line of the call that broke it, which matters when a function has many callers.

Checkpoint

Check: The four evaluation semantics

  1. Question 1 of 2 Built with -fcontract-evaluation-semantic=observe, what does percent(5, 4) do in the recorded run?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: It reports the broken precondition and the broken postcondition, then returns 125

    observe calls the handler for each violation and then carries on. The body computes 125, the postcondition r <= 100 is reported as broken as well, and 125 reaches the caller: observing reports bugs but does not stop their effects.

  2. Question 2 of 2 What does quick_enforce do differently from enforce when a contract is violated?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: It stops the program without calling the violation handler

    Both are terminating semantics. enforce calls the handler first, so a message or a log line can be written; quick_enforce stops at once, which keeps the checks small in the compiled program.

Practise: contracts and semantics

Eight questions follow on where the checks go and what each semantic does.

Practice that adapts to you

Practise this lesson

8 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 8 Where are pre(…) and post(…) written?

    Choose one answer.

    Show the answer: Question 1 of 8

    Answer: After the function's parameter list, before its body

    Preconditions and postconditions are written after the parameter list, before the body, so they are part of the function's interface: they appear in the header, next to the parameter list.

  2. Question 2 of 8 percent has two preconditions. In which order are they checked?

    Choose one answer.

    Show the answer: Question 2 of 8

    Answer: In the order they are written

    Preconditions are checked in the order written, then the body runs, then the postconditions on a normal return.

  3. Question 3 of 8 What does contract_assert(…) do differently from assert(…) inside a body?

    Choose one answer.

    Show the answer: Question 3 of 8

    Answer: It does not depend on whether NDEBUG happens to be defined; the build's semantic decides what it does

    contract_assert is a statement and can stand wherever a statement can, which makes it the replacement for assert inside a body, without the dependence on a macro such as NDEBUG.

  4. Question 4 of 8 Where does GCC 16 check a function's preconditions by default?

    Choose one answer.

    Show the answer: Question 4 of 8

    Answer: Inside the called function; -fcontracts-client-check=pre can add checks at call sites as well

    GCC checks preconditions and postconditions inside the called function by default, and its -fcontracts-client-check=pre (or =all) option adds the checks at call sites as well.

  5. Question 5 of 8 Which evaluation semantic does GCC 16 use when -fcontract-evaluation-semantic= is not given?

    Choose one answer.

    Show the answer: Question 5 of 8

    Answer: enforce

    GCC 16 chooses the evaluation semantic with -fcontract-evaluation-semantic=, and enforce is the default when the flag is missing, as the final draft recommends: the session's last build sets no semantic, and its report says semantic: enforce.

  6. Question 6 of 8 Built with ignore, what does percent(5, 4) return?

    Choose one answer.

    Show the answer: Question 6 of 8

    Answer: 125, and nothing is reported

    With ignore, percent(5, 4) quietly returns 125, a percentage above 100.

  7. Question 7 of 8 GCC's default handler reports the violation of percent(5, 4) at percent.cpp:7. What is on line 7?

    Choose one answer.

    Show the answer: Question 7 of 8

    Answer: The precondition that failed, not the call that broke it

    The default handler's two lines name the function, the file and line, the condition, the kind of assertion and the semantic. The line is the line of the precondition, not the line of the call that broke it.

  8. Question 8 of 8 A service is built with observe, and a caller breaks a precondition. What still happens after the violation is reported?

    Choose one answer.

    Show the answer: Question 8 of 8

    Answer: The call goes on, so whatever the bug causes still happens

    Observing tells you about bugs without preventing their effects: under observe, percent(5, 4) printed two reports and still returned 125.

Replacing the violation handler, and where GCC 16.2 differs from the final draft Pro: opens with a Pro, Premium or Ultimate pass

The default handler only prints. A program can install its own, to log violations in its own format or, in tests, turn them into exceptions. This part does both and records where GCC 16.2 departs from the final draft.

In this part:

  • A handler that logs one line and flushes, and the link step on macOS
  • Four ways GCC 16.2 departs from the final draft
  • A throwing handler for tests, and why noexcept stops it

7 runnable examples (C++, Python) · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Rules that catch people out, a compile-time check, and an exercise with a prediction Pro: opens with a Pro, Premium or Ultimate pass

Four rules of C++26 contracts surprise people who come from assert. Each is reproduced below with GCC 16.2’s message, and the exercise then asks you to predict the effect of contracts under two semantics before you run them.

In this part:

  • Four rules, from const parameters to constant evaluation, reproduced
  • Choosing a semantic for each build
  • Your turn: contracts for a bounded stack, with a prediction first

7 runnable examples (C header, C++, Python) · about 8 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

  1. Fresher to mid · Warm-up

    Name the four evaluation semantics of C++26 contracts, and say which call the violation handler and which stop the program.

    ignore does not check the contract at all, although the condition must still compile. observe checks it and calls the violation handler on a failure, then lets the program go on. enforce checks it, calls the handler, and stops the program when the handler returns. quick_enforce checks it and stops the program at once, without calling the handler. So observe and enforce call the handler, and enforce and quick_enforce stop the program. The semantic is chosen when building, not in the code: GCC 16 uses -fcontract-evaluation-semantic=, with enforce as the default, which is also what the final draft recommends.

  2. Senior to staff

    You want to add preconditions to a function that 400 call sites use, and some callers may already break them. How do you introduce the contracts without crashing production, and how do you decide when to switch to enforce?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    A function debits a wallet: debit(account_id, amount_paise). Which conditions belong in preconditions, which in a contract_assert, and which in an exception or an error result? Give one example of each and justify the choice.

    Model answer, Pro. First step and follow-ups

  4. Mid to senior

    Your builds enforce contracts, and enforce stops the program on a violation. How do you write unit tests that prove a precondition actually fires?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • pre(…) and post(r : …) attach checks to a function’s declaration; contract_assert(…) checks inside a body.
  • The evaluation semantic is a build choice: ignore, observe, enforce or quick_enforce, with enforce the default in GCC 16.
  • observe reports violations and lets the program go on, so the bug’s effects still happen.
  • Contracts state what a correct program never violates; validate user input with ordinary code.
  • GCC 16 accepts contracts with -std=c++26; Apple clang 21 does not.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.