requires în C++

Scrie cerințe pentru parametrii unui șablon: ca clauză care restrânge șablonul sau ca expresie care verifică dacă un cod este valid.

Sintaxă
template <typename T>
    requires condiție          // clauză requires
void f(T x);

requires(T x) { x.size(); }   // expresie requires: true dacă codul este valid

Ce face requires

Clauza requires se pune după lista de parametri a șablonului (sau după semnătura funcției) și conține o condiție la compilare: requires std::integral<T> && (sizeof(T) >= 4). Dacă tipul nu o îndeplinește, șablonul este ignorat pentru acel tip.

Expresia requires(parametri) { ... } dă true dacă tot codul dintre acolade este valid pentru tipurile date. Codul nu este executat, doar verificat. Poți cere și tipul rezultatului: { x.size() } -> std::convertible_to<std::size_t>;.

Cele două se combină des: requires requires(T x) { ... } (clauză care conține o expresie) sau, mai lizibil, o expresie salvată într-un concept cu nume. Expresia funcționează și într-un if constexpr.

Exemple

Exemplul 1: Clauză requires și supraîncărcare

Pentru tipurile întregi se alege prima variantă, pentru restul a doua. Clauza decide la compilare care funcție se potrivește.

#include <concepts>
#include <print>

template <typename T>
    requires std::integral<T>
void descrie(T x) {
    std::println("{} este întreg", x);
}

template <typename T>
    requires (!std::integral<T>)
void descrie(T x) {
    std::println("{} nu este întreg", x);
}

int main() {
    descrie(42);
    descrie(3.14);
    descrie("Iași");
}

Rezultat

42 este întreg
3.14 nu este întreg
Iași nu este întreg

Exemplul 2: Expresie requires în if constexpr

Funcția verifică la compilare dacă tipul are .size(). std::string și std::vector au, int nu.

#include <print>
#include <string>
#include <vector>

template <typename T>
void marime(const T& x) {
    if constexpr (requires { x.size(); }) {
        std::println("are {} elemente", x.size());
    } else {
        std::println("nu are size()");
    }
}

int main() {
    marime(std::string("Brașov"));
    marime(std::vector<int>{1, 2, 3});
    marime(2025);
}

Rezultat

are 7 elemente
are 3 elemente
nu are size()

Greșeli frecvente

  • Scrii requires !std::integral<T> fără paranteze. Gramatica cere paranteze în jurul expresiilor care nu sunt simple: requires (!std::integral<T>).
  • Crezi că o expresie requires execută codul. Codul doar este verificat dacă se compilează; nu are efecte.

Comenzi înrudite

  • concept: Dă un nume unui set de cerințe pentru tipuri, folosit ca să restrângi ce tipuri acceptă un șablon.
  • template: Scrie o funcție sau o clasă o singură dată, pentru orice tip; compilatorul generează varianta potrivită pentru fiecare tip folosit.
  • if constexpr: Un if evaluat la compilare: ramura falsă este eliminată complet, deci poate conține cod care nu s-ar compila pentru tipul curent.