std::optional în C++

O valoare care poate lipsi: conține fie un obiect de tip T, fie nimic (std::nullopt).

Sintaxă
#include <optional>
std::optional<T> o;              // gol
std::optional<T> o = valoare;
o = std::nullopt;                // golește
bool has_value() const noexcept;
auto o2 = std::make_optional<T>(argumente...);

Ce face std::optional

std::optional<T> este răspunsul modern la întrebarea „ce returnez când nu am rezultat?”. În loc de valori speciale (-1, șir gol, nullptr), funcția returnează un optional gol, iar apelantul trebuie să verifice.

Verificarea se face cu o.has_value() sau direct în if (o). Valoarea se citește cu *o (fără verificare), o.value() (aruncă excepție dacă e gol) sau o.value_or(implicit). std::nullopt este constanta care înseamnă „gol”.

std::make_optional(x) creează un optional cu tipul dedus din x. Obiectul este stocat direct în optional, fără alocare dinamică.

Ce returnează

Un obiect care fie conține o valoare de tip T, fie este gol. has_value() returnează bool.

Exemple

Exemplul 1: Căutare care poate să nu găsească nimic

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

std::optional<int> pozitie(const std::vector<std::string>& v, const std::string& cautat) {
    for (int i = 0; i < static_cast<int>(v.size()); ++i) {
        if (v[i] == cautat) {
            return i;
        }
    }
    return std::nullopt;
}

int main() {
    std::vector<std::string> orase{"Iași", "Cluj", "Brașov"};

    if (auto p = pozitie(orase, "Cluj")) {
        std::println("Cluj este pe poziția {}", *p);
    }
    auto q = pozitie(orase, "Arad");
    std::println("Arad găsit: {}", q.has_value());
}

Rezultat

Cluj este pe poziția 1
Arad găsit: false

Exemplul 2: Câmpuri opționale, nullopt și make_optional

#include <optional>
#include <print>
#include <string>

struct Persoana {
    std::string nume;
    std::optional<std::string> telefon;
};

void afiseaza(const Persoana& p) {
    std::println("{}: {}", p.nume, p.telefon ? *p.telefon : "fără telefon");
}

int main() {
    Persoana ana{"Ana", "0742 000 000"};
    Persoana ion{"Ion", std::nullopt};
    afiseaza(ana);
    afiseaza(ion);

    auto varsta = std::make_optional(25);   // std::optional<int>
    varsta = std::nullopt;
    std::println("are vârstă: {}", varsta.has_value());
}

Rezultat

Ana: 0742 000 000
Ion: fără telefon
are vârstă: false

Greșeli frecvente

  • Citești *o fără să verifici că o are valoare. Pe un optional gol, *o este comportament nedefinit; folosește if (o), value() sau value_or.
  • Scrii std::optional<T&>. Nu există (până în C++26); folosește un pointer sau std::optional<std::reference_wrapper<T>>.
  • Pentru std::optional<bool>, if (o) verifică dacă există valoare, nu dacă valoarea este true. Scrie if (o && *o) sau o.value_or(false).

Comenzi înrudite

  • std::optional::value: value() citește valoarea și aruncă excepție dacă optional e gol; value_or() dă o valoare implicită.
  • std::optional::and_then: Înlănțuiește operații pe un optional: and_then și transform rulează funcția doar dacă există valoare.
  • std::expected: Rezultatul unei operații care poate eșua: conține fie valoarea (T), fie motivul erorii (E).
  • std::variant: O valoare care are, la un moment dat, exact unul dintre mai multe tipuri posibile (union sigur).