constexpr în C++

Cere ca o valoare să fie calculată la compilare; o funcție constexpr poate rula atât la compilare, cât și la rulare.

Sintaxă
constexpr int N = 10;
constexpr int patrat(int x) { return x * x; }

Ce face constexpr

O variabilă constexpr este o constantă a cărei valoare trebuie cunoscută în momentul compilării. O poți folosi oriunde limbajul cere o constantă: mărimea unui std::array, un argument de șablon, un static_assert.

O funcție constexpr poate fi calculată de compilator când primește argumente constante, iar rezultatul ajunge direct în program, fără calcul la rulare. Cu argumente cunoscute doar la rulare, funcționează ca o funcție obișnuită.

Regulile s-au relaxat la fiecare standard: în C++11 o funcție constexpr putea avea doar un return; din C++14 poate avea bucle și variabile; din C++20 poate folosi chiar std::vector și std::string (temporar, în timpul calculului).

Exemple

Exemplul 1: Constante pentru mărimi

#include <array>
#include <print>

constexpr int ZILE = 7;
constexpr double PI = 3.14159265358979;

int main() {
    std::array<int, ZILE> temperaturi = {12, 14, 15, 13, 16, 18, 17};
    std::println("Zile măsurate: {}", temperaturi.size());
    std::println("Aria cercului cu raza 2: {:.4f}", PI * 2 * 2);
}

Rezultat

Zile măsurate: 7
Aria cercului cu raza 2: 12.5664

Exemplul 2: Funcție calculată la compilare

static_assert verifică rezultatul în timpul compilării: dacă factorial(5) n-ar fi 120, programul nici nu s-ar compila. Aceeași funcție este apoi folosită la rulare.

#include <print>

constexpr long long factorial(int n) {
    long long rezultat = 1;
    for (int i = 2; i <= n; ++i) {
        rezultat *= i;
    }
    return rezultat;
}

int main() {
    constexpr long long f5 = factorial(5);  // calculat de compilator
    static_assert(f5 == 120);
    std::println("5! = {}", f5);

    int n = 10;                              // valoare de la rulare
    std::println("{}! = {}", n, factorial(n));
}

Rezultat

5! = 120
10! = 3628800

Exemplul 3: Tabel generat la compilare

Tabloul de pătrate este construit în întregime de compilator; la rulare doar se afișează.

#include <array>
#include <print>

constexpr std::array<int, 6> patrate() {
    std::array<int, 6> t{};
    for (int i = 0; i < 6; ++i) {
        t[i] = i * i;
    }
    return t;
}

constexpr auto TABEL = patrate();

int main() {
    for (int valoare : TABEL) {
        std::print("{} ", valoare);
    }
    std::println("");
}

Rezultat

0 1 4 9 16 25

Greșeli frecvente

  • Inițializezi o variabilă constexpr cu o valoare de la rulare (constexpr int n = citeste();). Compilatorul refuză: pentru asta folosește const.
  • Crezi că o funcție constexpr rulează mereu la compilare. Doar când rezultatul este cerut într-un context constant; altfel poate rula normal. Dacă vrei garanția, folosește consteval.

Comenzi înrudite

  • const: Marchează o valoare care nu mai poate fi modificată după inițializare; compilatorul oprește orice încercare de schimbare.
  • consteval: Definește o funcție imediată: fiecare apel trebuie calculat la compilare, altfel programul nu se compilează.
  • constinit: Garantează că o variabilă globală sau statică este inițializată la compilare, dar fără să o facă constantă.
  • 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.
  • static_assert: Verifică o condiție la compilare; dacă este falsă, compilarea se oprește cu mesajul dat.