Metaprogramare la Compilare în C++20/C++23: Concepte (Concepts), constexpr, consteval și SFINAE Modern în C++

Ghid complet de calcul și metaprogramare la timpul compilării în C++20/C++23: definirea conceptelor cu clauza requires, înlocuirea SFINAE cu mesaje de eroare clare, funcții garantate la compilare (consteval) și if constexpr.

Sintaxă
template<typename T> concept Nume = requires(T a) { ... }; | consteval int f() { return 42; }

Metaprogramarea pe bază de șabloane (Template Metaprogramming) a fost întotdeauna una dintre cele mai puternice capabilități ale limbajului C++, dar și una dintre cele mai detestate din cauza sintaxei criptice bazate pe std::enable_if (idiomul SFINAE) și a erorilor de compilare de mii de linii indescifrabile. Standardul C++20 a transformat radical acest peisaj prin introducerea Conceptelor (Concepts): constrângeri formale, lizibile și de tip sigur verificate la timpul compilării.

1. Definirea și Utilizarea Propriilor Concepte

Un concept este o expresie booleană evaluată la timpul compilării care validează dacă un tip de date respectă un set de operațiuni (are anumite metode, permite anumiți operatori sau returnează anumite tipuri):

Definirea unui concept pentru tipuri care pot fi serializate în JSON
#include <concepts>
#include <string>
#include <print>

// Definim conceptul 'Serializabil':
template <typename T>
concept Serializabil = requires(T a) {
    { a.toJson() } -> std::same_as<std::string>; // Tipul trebuie să aibă o metodă toJson() ce returnează string
};

// Sintaxă concisă C++20 direct în lista de parametri:
void salveazaInBazaDeDate(const Serializabil auto& obiect) {
    std::println("Payload salvat: {}", obiect.toJson());
}

struct Utilizator {
    std::string nume;
    std::string toJson() const { return "{"nume": "" + nume + ""}"; }
};

struct ProdusFaraJson {
    int pret;
};

int main() {
    Utilizator u{"Maria"};
    salveazaInBazaDeDate(u); // ✅ Compilează perfect!

    // ProdusFaraJson p{100};
    // salveazaInBazaDeDate(p); 
    // ❌ Eroare de compilare curată: "constraints not satisfied: 'a.toJson()' is invalid"

    return 0;
}

2. constexpr vs consteval vs constinit

Specificator Standard Momentul Execuției Comportament și Utilitate
constexpr C++11 Compilare sau Runtime Funcția poate fi executată la compilare dacă parametrii sunt cunoscuți; altfel, este executată normal la runtime.
consteval C++20 Strict la Compilare (Immediate Function) Funcția trebuie garantat să se execute la timpul compilării. Dacă primește un parametru de runtime, compilatorul dă eroare! Zero cost la rulare.
constinit C++20 Inițializare la Compilare Garantează că o variabilă statică/globală este inițializată la timpul compilării, prevenind eroarea Static Initialization Order Fiasco.

3. Ramificare la Compilare cu if constexpr

Directiva if constexpr permite scrierea de algoritmi generici care se adaptează instantaneu la tipul primit, compilatorul eliminând complet ramurile neutilizate din binarul final:

template <typename T>
void afiseazaDetalii(const T& val) {
    if constexpr (std::is_pointer_v<T>) {
        std::println("Adresa pointer: {}", static_cast<const void*>(val));
    } else {
        std::println("Valoare directa: {}", val);
    }
}