# C++26 Contracts

11 september 2026 | https://www.ribomation.se/blog/2026/c++26-contracts/

Hur många gånger har du skrivit en kommentar ovanför en funktion, med liknande innebörd som denna "`// OBS! n måste vara >= 0`"? Vi vet alla hur ofta den läses. Nästa steg brukar bli ett `assert()` — som åtminstone kontrollerar saken, ända tills någon gör ett release-bygge med `-DNDEBUG` och kontrollen tyst försvinner. I C++26 finns äntligen ett tredje alternativ: skriv antagandet i själva signaturen av funktionen, där kompilatorn ser det, där den anropande koden ser det, och där det går att slå på och av utan att röra en enda rad kod.

I den här artikeln går jag igenom `pre()`, `post()` och `contract_assert()` — hur de skrivs, var de får sitta och vilka fällor som väntar. Vi tar en sväng förbi Tony Hoare 1969 och Bertrand Meyers programspråk Eiffel för att se varifrån idén kommer, bygger en ringbuffert där alla invarianter står i koden istället för i huvudet på den som skrev klassen, och kommer fram till det verkligt smarta greppet: vad som ska hända när ett kontrakt bryts står inte i källkoden — det bestämmer du när du kompilerar. På vägen dit tittar vi på hur man skriver en egen violation handler, vilka flaggor GCC 16 vill ha, det obegripliga länkfelet du får om du glömmer en av dem, och — minst lika viktigt — när man _inte_ ska använda kontrakt. All kod i artikeln är byggd och körd på riktigt, och finns på GitHub.

* * *

Vad är design by contract?
--------------------------

Tanken är lånad rakt av från affärsvärlden. När två parter skriver ett avtal står det svart på vitt vad var och en åtar sig, och vad var och en har rätt att förvänta sig tillbaka. Bryter någon mot sin del är det inte en olyckshändelse — det är ett avtalsbrott, och det är någons fel.

Överför det till en funktion, så faller tre begrepp ut nästan av sig själva:

*   **Precondition** — vad _anroparen_ lovar. `sqrt_int(n)` kräver att `n` inte är negativt. Uppfyller du inte det, har du brutit avtalet.
*   **Postcondition** — vad _funktionen_ lovar tillbaka, under förutsättning att anroparen höll sin del. `sqrt_int(n)` lovar att returnera ett icke-negativt tal.
*   **Invariant** — vad som gäller _hela tiden_ däremellan. En ringbuffert innehåller aldrig fler element än den har platser.

Det centrala — och det som är lätt att missa — är att det här inte är felhantering. Det är **ansvarsfördelning**. Ett kontrakt säger inte "här kan det gå fel och då gör vi så här". Det säger "det här kan inte inträffa, och gör det ändå har någon skrivit fel kod".

> Skillnaden mot undantag och `std::expected` är värd att stanna upp vid, för den avgör när du ska använda vilket. En fil som inte finns är _inte_ ett kontraktsbrott — filsystemet är utanför din kontroll, och att filen saknas är ett fullt förväntat utfall som programmet ska hantera snyggt. Ett negativt tal till `sqrt_int()` är däremot ett kontraktsbrott: ingen korrekt kod kan någonsin göra det anropet. Kontrakt handlar om **programmerarfel**. Undantag och `expected` handlar om **körningsfel**.

Har du programmerat ett tag har du redan gjort det här, fast för hand. Kommentaren ovanför funktionen:

```cpp
// OBS! n måste vara >= 0, annars odefinierat beteende
int sqrt_int(int n);
```

Det där är ett kontrakt. Problemet är bara att det är skrivet till en människa som inte läser det, kontrollerat av ingen, och osynligt för alla verktyg. Ett antagande som bara står i en kommentar är inte ett antagande — det är en förhoppning.

Nästa steg i den evolutionen brukar vara `assert()`, och det är ett riktigt steg framåt: nu kontrolleras det faktiskt. Men `assert()` har sina egna bekymmer, som vi återkommer till. Det som C++26 ger oss är samma idé, fast inbyggd i språket, synlig i signaturen, och med en av- och påslagsknapp som sitter i bygget istället för i koden.

* * *

Historien: från Hoare till C++26
--------------------------------

Idén är betydligt äldre än C++. Den är faktiskt äldre än C.

### 1969 — Hoare och den axiomatiska grunden

År 1969 publicerade **C.A.R. Hoare** (_Sir Charles An**tony** Richard **Hoare**_) artikeln _An Axiomatic Basis for Computer Programming_. Där introducerade han det som numera kallas **Hoare-trippeln**:

```text
{P} C {Q}
```

Läs det som: _om påståendet `P` gäller innan programsatsen `C` körs, så gäller påståendet `Q` efteråt._ `P` är preconditionen, `Q` är postconditionen, och `C` är koden däremellan. Hela poängen var att kunna resonera om programkorrekthet matematiskt istället för att testa sig fram.

Det är alltså exakt `pre` och `post` — formulerat 57 år innan C++ fick dem.

> Få personer har påverkat hur vi programmerar på fundamental nivå, i samma grad som Tony Hoare. Förutom det här med `pre` & `post`, så uppfann han quicksort algoritmen, formulerade CSP (_Communicating Sequential Processes_) som resulterade i programspråket Occam för Transputers (1980-talets motsvarighet till Raspberry Pi) och otaliga andra språk, och monitor-begreppet som ligger till grund för användandet av mutex-lock och condition-variable i fler-trådade program och mer därtill. Ja, just det; han uppfann också värdet `null` för pekare och flera decennier senare stod han på en konferens 2009 och sa att detta var hans _"billion-dollar mistake"_. Detta har man ju tagit till sig i Rust, där `null` saknas helt, till förmån för datatyper som `Option<T>` och `Result<T,E>`.

   ![C.A.R. Hoare](/blog/2026/c++26-contracts/tony-hoare-1774w.jpeg "C.A.R. Hoare")

### 1986 — Meyer och Eiffel

Hoares trippel var teori. Det var **Bertrand Meyer** som gjorde den till en programmeringsteknik. I samband med språket **Eiffel** (1986) myntade han begreppet _Design by Contract_ och byggde in det direkt i syntaxen, med nyckelorden `require`, `ensure` och `invariant`.

Så här ser det ut i Eiffel:

```eiffel
class ACCOUNT

feature -- Access
    balance: INTEGER
            -- Current balance

feature -- Element change
    deposit (sum: INTEGER)
            -- Add `sum' to account.
        require
            non_negative: sum >= 0
        do
            balance := balance + sum
        ensure
            updated: balance = old balance + sum
        end

invariant
    non_negative_balance: balance >= 0

end -- class ACCOUNT
```

Känns det bekant? Det borde det göra. `require` är `pre` och `ensure` är `post`. Skillnaden i uttryckskraft är dock större än den ser ut vid första anblicken, och det är värt att notera två saker som Eiffel hade redan 1986 och som C++26 **inte** har:

*   **`old`** — i post-condition kan man referera till värdet som något _hade vid inträdet_. `balance = old balance + sum` säger att saldot ska ha ökat med exakt insättningen. Det går inte att uttrycka i C++26 — ännu.
*   **Klassinvarianter** — ett villkor som kontrolleras efter varje publik medlemsfunktion, automatiskt. Vi får se när detta kommer till C++.

   ![Bertrand Meyer](/blog/2026/c++26-contracts/bertrand-meyer-1774w.jpeg "Bertrand Meyer")

### Mellanspelet: `assert()` och alla hemmabyggen

C fick `assert()` i C89, och den har tjänat oss troget i snart fyrtio år. Jag har själv strött den över mer kod än jag vill kännas vid. Men den har fyra irriterande egenskaper:

1.  Den syns inte i signaturen. Den ligger inuti en funktion, dit anroparen aldrig tittar.
2.  Den har exakt två lägen: på eller av, via `NDEBUG`. Inget däremellan.
3.  Om villkoret är brutet, så avbryts programmet. Punkt. Du kan inte logga och fortsätta.
4.  Du kan inte styra vad som händer — ingen egen hanterare, ingen egen formatering.

Resultatet blev, som alltid när standarden inte räcker till, att varenda kodbas byggde sin egen. `MY_ASSERT`, `VERIFY`, `CHECK`, `REQUIRE`, `ASSERT_MSG`, och så `Expects`/`Ensures` från GSL för dem som läst Core Guidelines. Alla gör ungefär samma sak, ingen gör det likadant, och ingen av dem går att slå av och på utifrån.

### 2018–2019 — contracts i C++20, och ut igen

Contracts röstades faktiskt **in** i C++20. Och sedan **ut** igen.

På mötet i Köln i juli 2019 — feature freeze för C++20 — gjordes en sen och omfattande designändring, och förtroendet för hela förslaget rasade ihop. Resultatet blev **P1823R0**, med den föredömligt kortfattade titeln _"Remove Contracts from C++20"_. Det är en av de mer dramatiska vändningarna i kommitténs historia.

Kärnfrågan man inte kunde enas om var enkel att ställa och kolossalt svår att besvara: **vad ska hända när ett kontrakt bryts?** Terminera? Kasta ett undantag? Logga och fortsätta? Olika läger hade olika — och förvisso helt legitima — behov. Ett inbyggt system vill terminera direkt. En testsvit vill logga allt och köra vidare. En release-build vill inte betala ett dugg.

Efter urröstningen bildades studiegruppen **SG21** med ett enda uppdrag: gör om, gör rätt. Nästan sex år senare landade **P2900**, som röstades in i C++26.

Och lösningen på kärnfrågan? Man valde helt enkelt **att inte välja**. Vad som händer vid ett kontraktsbrott är inte en egenskap hos koden — det är en egenskap hos _bygget_. Det är den designen som gör denna språknyhet riktigt intressant.

> Idén är för övrigt långt ifrån unik för C++. Ada har `Pre`/`Post` sedan Ada 2012, D har `in`/`out`/`invariant`, Kotlin har `require`/`check`, Clojure har `:pre`/`:post`, och .NET hade ett tag Code Contracts. C++ är snarare sent på bollen än tidigt.

* * *

Syntaxen: `pre()` och `post()`
------------------------------

Nog med historia. Så här ser det ut för en funktion som begränsar ett värde (`v`) till att ligga inom ett intervall (`lo..hi`):

```cpp
int clamp(int v, int const lo, int const hi)
    pre(lo <= hi)
    post(r: lo <= r and r <= hi)
{
    return v < lo ? lo : (v > hi ? hi : v);
}
```

Kontrakten står **mellan signaturen och kroppen** — på samma plats som `noexcept` och `override` skulle ha stått. Det är ingen slump: det är just där de hör hemma, som en del av funktionens gränssnitt och inte av dess implementation.

Punkt för punkt:

*   **`pre(expr)`** kontrolleras vid inträdet i funktionen, innan kroppen körs.
*   **`post(expr)`** kontrolleras vid utträdet, efter att kroppen är klar.
*   **`post(r: expr)`** binder returvärdet till namnet `r`, så att du kan uttala dig om det. Och `r` är **bara en identifierare** — inget nyckelord. `post(result: ...)` eller `post(x: ...)` fungerar precis lika bra, vilket vi kommer att utnyttja senare.
*   Behöver du inte returvärdet skriver du bara `post(expr)` utan bindning, till exempel `post(not empty())`.
*   Du kan ha **flera** av varje. `pre(a) pre(b)` är fullt tillåtet och läser ofta bättre än `pre(a and b)`, eftersom felmeddelandet då pekar ut exakt vilket villkor som brast.

Predikaten är helt vanlig C++. Det finns inget minispråk att lära sig — du kan anropa funktioner, använda operatorer, läsa medlemsvariabler. Enda kravet är att uttrycket går att konvertera till `bool`.

### Fällan: `const` på by-value-parametrar

Här går man på en mina direkt, och det är värt att gå på den medvetet istället för i skarpt läge. Följande ser fullständigt rimligt ut:

```cpp
int clamp(int v, int lo, int hi)
    pre(lo <= hi)
    post(r: lo <= r and r <= hi)
{
    return v < lo ? lo : (v > hi ? hi : v);
}
```

Men det kompilerar inte:

```shell
$ g++ -std=c++26 -fcontracts -c clamp.cxx
clamp.cxx:3:13: error: a value parameter used in a postcondition must be const
    3 |     post(r: lo <= r and r <= hi)
      |             ^~
clamp.cxx:1:22: note: parameter declared here
    1 | int clamp(int v, int lo, int hi)
      |                  ~~~~^~
```

> Varför så sträng? För att funktionskroppen får ändra sina egna parameterkopior hur den vill. Skrev vi `lo = 0;` någonstans i kroppen skulle postconditionen plötsligt mäta mot ett annat `lo` än det anroparen skickade in — och då lovar funktionen något helt annat än vad signaturen påstår. `const` blockerar den möjligheten.

Lösningen är enkel: sätt `const` på de parametrar du nämner i din postcondition. Notera att `v` klarar sig utan, eftersom den inte förekommer där.

```cpp
int clamp(int v, int const lo, int const hi)
```

Detta gäller bara **by value**. Parametrar som tas som `const&` är redan konstanta, och för dem är det här ett icke-problem.

### Var kontrakt får sitta

Fria funktioner, medlemsfunktioner, konstruktorer — och, lite oväntat, **lambdor**:

```cpp
auto half = [](int const n) pre(n % 2 == 0) post(r: 2 * r == n) { return n / 2; };
```

Det fungerar precis som man hoppas. Däremot finns en begränsning som är lätt att gå in i:

```shell
$ g++ -std=c++26 -fcontracts -c shape.cxx
shape.cxx:3:18: error: contracts cannot be added to virtual functions
    3 |     virtual auto area() const -> double post(r: r > 0.0);
      |                  ^~~~
```

**Kontrakt på virtuella funktioner finns inte i C++26.** Det är inte någon kompilatorbrist utan ett medvetet beslut. Motiveringen i **P2899** är ovanligt rakt på sak om saken:

> _"The ability to specify precondition and postcondition assertions on virtual functions is important for adopting contract assertions into codebases that make use of runtime polymorphism. Nevertheless, \[P2900R14\] intentionally omits this feature. \[…\] We expect that this feature will be added as a future extension to the Contracts proposal."_

Det är alltså inte _om_, utan _när_. P3097R3 antogs in i utkastet till C++29 på Brno-mötet i juni 2026. Herb Sutters reserapport: "_less than three months after C++26 was technically finalized, draft standard C++(29) contracts do support virtual functions._"

### Deklaration kontra definition

Ett kontrakt kan — och bör ofta — sitta på **deklarationen** i header-filen. Då blir det en del av det publicerade API:t, synligt för varenda anropare som inkluderar headern:

```cpp
// int-sqrt.hxx
#pragma once

int int_sqrt(int const n)
    pre(n >= 0)
    post(r: r >= 0);
```

Regeln som gäller är att **alla deklarationer av en funktion måste bära samma kontrakt**. Lägger du till en `post` i `.cxx`\-filen som headern inte har, blir det kompileringsfel:

```shell
error: declaration has a different number of contracts than previously declared
note: previous declaration with fewer contracts here
```

Du får antingen upprepa dem identiskt i definitionen, eller utelämna dem helt där. Båda varianterna är lagliga, och vi kommer strax till varför man kanske vill upprepa dem.

* * *

`contract_assert()` — invarianter mitt i koden
----------------------------------------------

`pre` och `post` sitter på funktionens gräns. Men ibland vill man hävda något _mitt inne_ i en kropp — en loopinvariant, ett antagande efter en beräkning. Då används `contract_assert()`:

```cpp
int sum_to(int const n)
    pre(n >= 0)
    post(r: r >= 0)
{
    auto total = 0;
    for (auto k = 1; k <= n; ++k) {
        total += k;
        contract_assert(total > 0);   // loop invariant
    }
    return total;
}
```

Det här är den standardiserade ersättaren för `assert()`. Men det viktiga är inte att den gör samma sak — det är att den gör det **på samma villkor som `pre` och `post`**. Samma byggflagga styr den, samma hanterare tar emot brottet, samma formatering på utskriften. Ett enda system istället för två.

`assert()`

`contract_assert()`

Styrs av

`NDEBUG`, av eller på

Fyra utvärderingssemantiker

Vid brott

Alltid `abort()`

Konfigurerbart per bygge

Logga och fortsätta

Nej

Ja, med `observe`

Egen hanterare

Nej

Ja

Diagnostik i compile-time

Ja, men som `__assert_fail`

Ja, med predikatet i klartext

Standardiserad

Ja, sedan C89

Ja, från C++26

Notera särskilt den näst sista raden. Vi kommer tillbaka till den, för den är roligare än den låter.

* * *

Demot: `CircularBuffer<T, N>`
-----------------------------

Dags att sätta ihop delarna till något som liknar riktig kod. En ringbuffert är ett bra exempel, eftersom den är full av invarianter som är lätta att formulera i ord och lätta att slarva bort i koden.

   ![Illustration of a ring buffer](/blog/2026/c++26-contracts/ring-buffer-1774w.jpeg "Illustration of a ring buffer")

### Konstruktorn

```cpp
template<typename ElemType, unsigned CAPACITY = 10U>
class CircularBuffer {
    std::array<ElemType, CAPACITY> buffer{};
    unsigned put_idx{}, get_idx{}, num_elems{};

public:
    CircularBuffer() pre(CAPACITY >= 3U) { }
```

Ett kontrakt på en **template-parameter**. En ringbuffert med plats för två element är knappast meningsfull, och istället för att skriva det i en kommentar — eller, ännu värre, låta bli — står det nu i koden. I just fallet med en template-parameter kan man förvisso använda en `static_assert()`, men då missar vi poängen i denna artikel.

### `put()`

```cpp
    void put(ElemType const& x)
        pre(put_idx < capacity() and not full())
        post(not empty())
    {
        buffer[put_idx] = x;
        ++put_idx %= capacity();
        ++num_elems;
        contract_assert(size() <= capacity());
    }
```

Tre kontrakt på några rader kod, och alla tre säger något som annars bara hade funnits i huvudet på den som skrev klassen:

*   Insättningsindexet ligger inom bufferten, och det finns plats kvar.
*   Efteråt är bufferten garanterat inte tom — vi har ju precis lagt in något.
*   Antalet element överskrider aldrig kapaciteten.

Lägg märke till att predikaten anropar klassens egna `const`\-metoder. Kontrakt är vanlig C++, och `full()`, `empty()` och `size()` är precis de begrepp man ändå tänker i termer av.

### `get()`

```cpp
    auto get() -> ElemType
        pre(get_idx < capacity() and not empty())
        post(x: x == prev() and not full())
    {
        auto x = buffer[get_idx];
        ++get_idx %= capacity();
        --num_elems;
        contract_assert(size() < capacity());
        return x;
    }
```

Här blir det intressant. Postconditionen `post(x: x == prev() and not full())` binder returvärdet till `x` och jämför det mot en **privat hjälpmetod**:

```cpp
private:
    auto& prev() const {
        return buffer.at((get_idx + capacity() - 1) % capacity());
    }
```

`prev()` pekar ut elementet precis före det uppdaterade läsindexet — alltså det som nyss lämnades ut. Postconditionen verifierar därmed att vi faktiskt returnerade _rätt_ element, inte bara _ett_ element. Det är precis den sortens kontroll man vet att man borde skriva ett test för, och precis den sortens test man aldrig kommer till skott med.

Notera också att jag kallade bindningen `x` här och `r` i `clamp()`. Som sagt: det är bara ett namn.

### Körningen

Huvudprogrammet gör tre saker: en normal genomkörning, en overflow, och ett försök att skapa en buffert som är för liten.

```cpp
int main() {
    {
        std::println("--- Normal operation ---");
        auto buf = CircularBuffer<int, 5>{};
        for (auto k = 1; not buf.full(); ++k) buf.put(k);
        for (auto first = true; not buf.empty(); first = false)
            std::print("{}{}", (first ? "" : ", "), buf.get());
        std::println();
    }
    {
        std::println("--- Overflow: one put() too many ---");
        auto buf = CircularBuffer<int, 3>{};
        for (auto k = 1; not buf.full(); ++k) buf.put(k);
        buf.put(666);
    }
    {
        std::println("--- Invalid capacity: too small ---");
        auto _ = CircularBuffer<int, 1>{};
    }
    std::println("--- Done ---");
}
```

Byggt och kört med semantiken `observe`, som loggar brottet och låter programmet fortsätta:

```shell
$ g++ -std=c++26 -Wall -Wextra -fcontracts \
      -fcontract-evaluation-semantic=observe \
      src/circular-buffer.cxx -o circular-buffer
$ ./circular-buffer
--- Normal operation ---
1, 2, 3, 4, 5
--- Overflow: one put() too many ---
contract violation in function void CircularBuffer<ElemType, CAPACITY>::put(const ElemType&) [with ElemType = int; unsigned int CAPACITY = 3] at src/circular-buffer.cxx:16: put_idx < capacity() and not full()
[assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no]
contract violation in function void CircularBuffer<ElemType, CAPACITY>::put(const ElemType&) [with ElemType = int; unsigned int CAPACITY = 3] at src/circular-buffer.cxx:22: size() <= capacity()
[assertion_kind: assert, semantic: observe, mode: predicate_false, terminating: no]
--- Invalid capacity: too small ---
contract violation in function CircularBuffer<ElemType, CAPACITY>::CircularBuffer() [with ElemType = int; unsigned int CAPACITY = 1] at src/circular-buffer.cxx:13: CAPACITY >= 3U
[assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no]
--- Done ---
```

Titta noga på overflow-fallet. Det smäller **två gånger**:

1.  Först `pre(put_idx < capacity() and not full())` — bufferten var full, anroparen bröt avtalet.
2.  Sedan `contract_assert(size() <= capacity())` — eftersom `observe` betyder "logga och kör vidare" fortsatte `put()` att köra, `num_elems` räknades upp till 4, och nu rymmer en buffert med tre platser plötsligt fyra element.

Man ser buggen **fortplanta sig**. Kontrakt nummer ett visar var felet uppstod, kontrakt nummer två visar vad det ledde till. Det är precis det `observe` är till för, och det är något `assert()` aldrig kunnat ge dig — den hade tagit kål på processen vid punkt ett.

* * *

Fyra semantiker — valet görs vid bygget
---------------------------------------

Nu kommer vi till designbeslutet som fick hela förslaget att gå i lås. Vad som händer vid ett kontraktsbrott står **inte i koden**. Det bestäms när du kompilerar.

Semantik

Utvärderas?

Anropar hanteraren?

Terminerar?

Typiskt användningsfall

`ignore`

Nej

–

Nej

Release. Noll overhead.

`observe`

Ja

Ja

Nej

Införande i befintlig kodbas, testkörningar

`enforce`

Ja

Ja

Ja

Debug- och CI-byggen. **Default i GCC.**

`quick_enforce`

Ja

**Nej**

Ja

Säkerhetskritiskt, minimal kodstorlek

Samma källkod, fyra beteenden. Här är samma ringbuffert byggd fyra gånger:

**`ignore`** — kontrakten kompileras bort helt:

```shell
$ ./circular-buffer-ignore
--- Normal operation ---
1, 2, 3, 4, 5
--- Overflow: one put() too many ---
--- Invalid capacity: too small ---
--- Done ---
```

**`observe`** — loggar och fortsätter, som vi såg ovan.

**`enforce`** — loggar och terminerar:

```shell
$ ./circular-buffer-enforce
--- Normal operation ---
1, 2, 3, 4, 5
--- Overflow: one put() too many ---
contract violation in function void CircularBuffer<ElemType, CAPACITY>::put(const ElemType&) [with ElemType = int; unsigned int CAPACITY = 3] at src/circular-buffer.cxx:16: put_idx < capacity() and not full()
[assertion_kind: pre, semantic: enforce, mode: predicate_false, terminating: yes]
terminate called without an active exception
Aborted (core dumped)
$ echo $?
134
```

**`quick_enforce`** — terminerar direkt, utan att ens anropa hanteraren:

```shell
$ ./circular-buffer-quick_enforce
--- Normal operation ---
1, 2, 3, 4, 5
--- Overflow: one put() too many ---
terminate called without an active exception
Aborted (core dumped)
```

Det är `observe` som är det verkliga nytillskottet. Den semantiken gör det praktiskt möjligt att dra på kontrakt över en befintlig kodbas utan att allt genast slutar fungera. Bygg med `observe`, kör testsviten, samla in loggen, och se hur illa det står till — _innan_ du slår på `enforce` och börjar terminera processer.

> **Praktisk fälla.** `abort()` flushar inte en buffrad `stdout`. Under `enforce` eller `quick_enforce` kan därför hela utskriften _före_ kraschen försvinna, särskilt om du pipear output till en fil. Första gången jag såg det trodde jag att programmet dog direkt vid start. Demoprogrammen sätter därför radbuffring i början av `main()`:
> 
> ```cpp
> std::setvbuf(stdout, nullptr, _IOLBF, 0);
> ```
> 
> `_IOLBF` innebär radbuffring (I/O Line Buffering), det vill säga "töm efter varje `\n`".

* * *

Kompilatorstöd och byggflaggor
------------------------------

Läget hösten 2026 är att **GCC 16** är det enda man kan arbeta med på riktigt. Jag har verifierat att både en egenbyggd `gcc-16.1`\-release och Ubuntus trunk-snapshot `g++-16` klarar allt i den här artikeln.

> Det cirkulerar en del blogginlägg som påstår att man behöver bygga en särskild contracts-gren av GCC. Det stämde ett tag, men gäller inte längre — contracts ligger i mainline GCC 16. Clang har en experimentell gren på gång, MSVC har såvitt jag kunnat utröna ingenting ännu.

Grundkommandot ser ut så här:

```shell
$ g++ -std=c++26 -Wall -Wextra -Wpedantic \
      -fcontracts \
      -fcontract-evaluation-semantic=observe \
      src/circular-buffer.cxx -o circular-buffer
```

Flaggorna som finns:

Flagga

Betydelse

`-fcontracts`

Slår på hela finessen. Behövs **även vid länkning**.

`-fcontract-evaluation-semantic=`

`ignore` | `observe` | `enforce` | `quick_enforce`. Default: `enforce`.

`-fcontracts-client-check=`

`none` | `pre` | `all`. Kontrollera även hos anroparen. Default: `none`.

`-fcontracts-definition-check=`

`on` | `off`. Kontrollera inuti funktionsdefinitionen. Default: `on`.

### Väggen du kommer att gå in i

Av allt i den här artikeln är det här det mest sannolika sättet att fastna, och felmeddelandet hjälper inte ett dugg:

```shell
$ g++ -std=c++26 -fcontracts -fcontract-evaluation-semantic=observe \
      -c src/circular-buffer.cxx -o circular-buffer.o
$ g++ circular-buffer.o -o circular-buffer
/usr/bin/ld: circular-buffer.o: in function `__tu_has_violation(...)':
circular-buffer.cxx:(.text+0x266): undefined reference to
  `handle_contract_violation(std::contracts::contract_violation const&)'
collect2: error: ld returned 1 exit status
```

Inte ett ord om contracts. Bara en saknad symbol med ett namn som inte finns någonstans i din kod.

Förklaringen är att `-fcontracts` vid **länkningen** är det som drar in standardhanteraren. Utelämnar du den där, och inte har skrivit en egen hanterare, står länkaren utan `handle_contract_violation`. Lägg till flaggan i länksteget också, så går det:

```shell
$ g++ -fcontracts circular-buffer.o -o circular-buffer
```

I CMake blir det motsvarande:

```cmake
target_compile_options(my-app PRIVATE
        -fcontracts
        -fcontract-evaluation-semantic=observe)
target_link_options(my-app PRIVATE -fcontracts)
```

### Klientsideskontroll

Som standard kontrollerar GCC bara preconditionen **inuti** funktionsdefinitionen. Men minns det ursprungliga försäljningsargumentet: _"ett brutet kontrakt rapporteras vid anropsstället"_. Det är vad `-fcontracts-client-check=pre` ger dig.

Ta ett litet bibliotek i två filer. Header-filen deklarerar kontraktet:

```cpp
// src/client-check/int-sqrt.hxx
int int_sqrt(int const n)
    pre(n >= 0)
    post(r: r >= 0);
```

Och definitionen upprepar det:

```cpp
// src/client-check/int-sqrt.cxx
int int_sqrt(int const n)
    pre(n >= 0)
    post(r: r >= 0)
{
    return static_cast<int>(std::sqrt(n));
}
```

Slutligen, i `main()` anropar vi med argumenten `49` respektive `-4` och ser vad som händer:

```cpp
std::println("int_sqrt(49) = {}", int_sqrt(49));
std::println("int_sqrt(-4) = {}", int_sqrt(-4));
```

Med `-fcontracts-client-check=none` (default) kontrolleras `pre` bara på ett ställe:

```shell
$ ./client-check-none
int_sqrt(49) = 7
contract violation in function int int_sqrt(int) at src/client-check/int-sqrt.cxx:10: n >= 0
[assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no]
contract violation in function int int_sqrt(int) at src/client-check/int-sqrt.cxx:11: r >= 0
[assertion_kind: post, semantic: observe, mode: predicate_false, terminating: no]
int_sqrt(-4) = -2147483648
```

Med `=pre` kontrolleras den **två** gånger — en gång hos anroparen och en gång i definitionen:

```shell
$ ./client-check-pre
int_sqrt(49) = 7
contract violation in function int int_sqrt(int) at src/client-check/int-sqrt.hxx:7: n >= 0
[assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no]
contract violation in function int int_sqrt(int) at src/client-check/int-sqrt.cxx:10: n >= 0
[assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no]
contract violation in function int int_sqrt(int) at src/client-check/int-sqrt.cxx:11: r >= 0
[assertion_kind: post, semantic: observe, mode: predicate_false, terminating: no]
int_sqrt(-4) = -2147483648
```

Det är just därför det kan löna sig att upprepa kontrakten i definitionen: filnamn och radnummer skiljer de två kontrollerna åt, så du ser direkt vilken som var vilken. Hade kontrakten bara stått i headern hade båda raderna pekat på headern.

### Kontrakt vid kompileringstid

Det här är en cool detalj. Kontrakt fungerar i `constexpr`\-sammanhang, och då får man **statisk analys, gratis**:

```cpp
constexpr int fact(int const n)
    pre(n >= 0)
{
    return n <= 1 ? 1 : n * fact(n - 1);
}

int main() {
    constexpr auto bad = fact(-1);
    return bad;
}
```

```shell
$ g++ -std=c++26 -fcontracts -fcontract-evaluation-semantic=enforce fact.cxx
fact.cxx: In function 'int main()':
fact.cxx:2:5: error: contract predicate is false in constant expression
    2 |     pre(n >= 0)
      |     ^~~~~~~~~~~
```

Ett brutet kontrakt i ett konstantuttryck blir ett **kompileringsfel** under `enforce`, och en varning under `observe`. Programmet kommer aldrig ens i närheten av att köras.

> En liten sak som kan spara någon en halvtimmes felsökning: feature-test-makrot `__cpp_contracts` är i GCC 16.1 definierat till `202502` **även utan** `-fcontracts`. Det duger alltså inte som villkor för att avgöra om finessen är påslagen.

* * *

Egen violation handler
----------------------

Standardhanterarens utskrift är korrekt, men utskriften underlättar inte direkt en snabb analys av vad som har blivit fel:

```
contract violation in function void CircularBuffer<ElemType, CAPACITY>::put(const ElemType&) [with ElemType = int; unsigned int CAPACITY = 3] at src/circular-buffer.cxx:16: put_idx < capacity() and not full()
[assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no]
```

Hela template-instansieringen, absolut sökväg, och predikatet allra sist på raden. I ett riktigt projekt vill du antagligen ha det i ditt eget loggformat, med ditt eget request-id påklistrat, och kanske en räknare i din metrics-uppsättning.

Det går, och det är förbluffande enkelt. Deklarera en global funktion med exakt den här signaturen:

```cpp
void handle_contract_violation(std::contracts::contract_violation const& v);
```

Det är allt. Ingen registrering, inget makro, ingen header som resten av programmet behöver inkludera. Finns symbolen vid länkningen väljs din hanterare, annars standardens.

### Vad du får veta

Objektet `std::contracts::contract_violation` bär all information om brottet:

Egenskap

Returtyp

Beskrivning

`kind()`

`assertion_kind`

`pre` | `post` | `assert`

`semantic()`

`evaluation_semantic`

`ignore` | `observe` | `enforce` | `quick_enforce`

`is_terminating()`

`bool`

`true` om programmet kommer att avslutas

`mode()`

`detection_mode`

`predicate_false` | `evaluation_exception`

`comment()`

`const char*`

Predikatet som text

`location()`

`source_location`

Fil, rad och funktionsnamn

> **Två avvikelser att känna till.** P2900 och cppreference kallar den fjärde egenskapen `detection_mode()`, men libstdc++ i GCC 16.1 stavar den `mode()`. Och även om `detection_mode::evaluation_exception` finns i headern, så rapporterar GCC 16.1 `predicate_false` även när predikatet kastar ett undantag. Jag har provat. Det är sådant som ger sig med tiden, men just nu får man dras med "skönhetsfelet".

### En hanterare som gör jobbet

```cpp
#include <print>
#include <string_view>
#include <contracts>

using namespace std::contracts;

namespace {
    constexpr auto to_text(assertion_kind k) -> std::string_view {
        switch (k) {
            case assertion_kind::pre:    return "pre";
            case assertion_kind::post:   return "post";
            case assertion_kind::assert: return "assert";
        }
        return "?";
    }

    // Drop the absolute project-folder prefix, keeping the path from "src/" on.
    constexpr auto strip_project_dir(std::string_view path) -> std::string_view {
        if (auto const pos = path.rfind("/src/"); pos != std::string_view::npos)
            return path.substr(pos + 1);
        return path;
    }
}

void handle_contract_violation(contract_violation const& v) {
    auto const loc = v.location();
    std::println("[{}] {}  ({}:{})",
                 to_text(v.kind()),
                 v.comment(),
                 strip_project_dir(loc.file_name()),
                 loc.line());
}
```

Samma brott som ovan, men nu betydligt mer läsvärt:

```
[pre] put_idx < capacity() and not full()  (src/circular-buffer.cxx:16)
```

Om du inte kan bestämma dig och vill ha både och, så kan du från din egen handler anropa standardens version via:

```cpp
std::contracts::invoke_default_contract_violation_handler( v );
```

### Vad hanteraren inte får göra

Här gäller det att hålla tungan rätt i mun. Din hanterare anropas när programmet **redan befinner sig i ett trasigt tillstånd** — något som skulle vara omöjligt har just visat sig vara möjligt. Det betyder:

*   **Allokera inte.** Är det heapen som är korrupt gör du bara saken värre.
*   **Kasta inte undantag.** Under `enforce` är du på väg mot `terminate()` ändå.
*   **Anropa inte tillbaka in i koden som bröt kontraktet.** Vägen till obegränsad rekursion är kort.
*   **Håll den snabb.** Under `observe` kan den anropas väldigt många gånger.

### Och varför den inte ligger i ett bibliotek

En rimlig tanke är att paketera ihop hanteraren med sitt loggramverk och distribuera den som en del av ett bibliotek. Det fungerar dåligt, och anledningen är mekanismen själv: `handle_contract_violation` är **en enda global symbol för hela programmet**. Länkar du in två bibliotek som båda tagit med sin egen hanterare får du en _duplicate symbol_, och även om det går igenom är det inte längre applikationens eget val vilken som vinner.

Hanteraren hör därför hemma i **applikationens sista länksteg** — hos den som äger huvudprogrammet. Det är därför den ligger som en fristående `.cxx`\-fil i demoprojektet och inte i en header. Ett bibliotek kan gott _erbjuda_ en hanterare, men det ska vara applikationen som aktivt väljer att länka in den.

* * *

När ska man använda kontrakt — och när inte?
--------------------------------------------

Som brukligt när man är mjukvaruingenjör så gäller det att veta _när_ man ska applicera en idé och när så inte är fallet.

**Använd kontrakt för:**

*   API-antaganden som idag står i en kommentar eller ingenstans alls.
*   Interna invarianter — "det här kan aldrig inträffa".
*   Gradvis införande i en äldre kodbas, med `observe` som första steg.
*   Att ersätta hemmabyggda `ASSERT`\-makron med något standardiserat.

**Använd inte kontrakt för:**

*   Validering av användarindata. Att någon skriver "abc" i ett åldersfält är inte ett programmerarfel.
*   Fel från filsystem, nätverk eller databaser. De är förväntade och ska hanteras.
*   Något som får hända i produktion. Bryts ett kontrakt är programmet trasigt — då är `std::expected` eller ett undantag (_exception_) rätt verktyg, inte ett kontrakt.

Tumregeln: **kan en korrekt anropare någonsin utlösa villkoret, är det inte ett kontrakt.**

Två praktiska råd på köpet. Håll predikaten **enkla och fria från sidoeffekter** — ett `pre()` som anropar en komplex funktion betalar du för vid varje anrop under `observe` och `enforce`. Och skriv hellre `pre(a) pre(b)` än `pre(a and b)`, så pekar felmeddelandet ut exakt vilket villkor som brast.

* * *

Vad som inte kom med
--------------------

Det som röstades in i C++26 är uttryckligen en **MVP** — _minimum viable product_. Motiveringen i **P2899** listar vad som medvetet lämnades utanför, och det är värt att känna till innan man blir besviken:

*   **Kontrakt på virtuella funktioner.** Planerat som framtida utvidgning.
*   **`old`\-värden i postconditions.** Rationalet tar upp precis det exempel man först tänker på: postconditionen för `std::vector::push_back` — att storleken ska ha ökat med ett — går inte att uttrycka i C++26.
*   **Klassinvarianter.** Det som Eiffel hade 1986. Bedömdes ha för många öppna frågor för att hålla upp hela finessen.
*   **Etiketter på kontrakt**, för att välja semantik per enskilt kontrakt istället för per bygge.
*   **Kontrakt på funktionspekare** och typalias till sådana.

Det är, med andra ord, en första version. Men det är en genomtänkt första version, och — till skillnad från C++20-försöket — en som faktiskt gick i mål.

Specifikationen är för övrigt betydligt trevligare läsning än man kan tro, och väl värt en kvart om du undrar varför någon detalj ser ut som den gör. Det, och alla andra referenser i artikeln, hittar du under _Länkar_ sist på sidan.

   ![C++ standard committee voting for contracts](/blog/2026/c++26-contracts/c++-std-committee-1774w.jpeg "C++ standard committee voting for contracts")

* * *

Sammanfattning
==============

Vi har rest en rätt lång sträcka: från Hoares `{P} C {Q}` 1969, via Eiffels `require` och `ensure`, genom `assert()` och fyrtio år av hemmabyggda makron, förbi den pinsamma urröstningen ur C++20, och fram till `pre`, `post` och `contract_assert` i C++26.

Det som är nytt är inte idén. Idén är äldre än de flesta av oss. Det som är nytt är att antagandet äntligen står **i signaturen** istället för i kommentaren ovanför — där kompilatorn ser det, där anroparen ser det, och där det går att kontrollera.

Och det verkligt smarta greppet, det som fick förslaget att gå i mål den här gången, är att C++26 vägrar svara på frågan "vad ska hända vid ett kontraktsbrott?". Det svaret hör inte hemma i koden. `ignore` i release, `observe` när du inför kontrakt i en gammal kodbas, `enforce` i CI. Samma källkod, olika stränghet, inget att kommentera bort.

Är det produktionsklart? Tja, det beror på. Språkdelen fungerar förvånansvärt bra — jag har inte lyckats få GCC att göra något oväntat med själva semantiken — men runt omkring skaver det. Länkfelet utan förklaring, `mode()` som skulle hetat `detection_mode()`, `evaluation_exception` som aldrig rapporteras, feature-test-makrot som är påslaget även när finessen är avslagen. Inget av det är blockerande, men allt av det kostar dig en kvart första gången.

Mitt råd: klona demot — länken finns sist på sidan — bygg det med alla fyra semantikerna, och lek en stund. Ta sedan en klass i din egen kod — helst en med invarianter du redan har i huvudet men aldrig skrivit ner — och sätt `pre` och `post` på den. Bygg med `observe` och kör din testsvit.

Jag gissar att du blir förvånad över vad som ramlar ut.
