std::variant în C++

O valoare care are, la un moment dat, exact unul dintre mai multe tipuri posibile (union sigur).

Sintaxă
#include <variant>
std::variant<T1, T2, ...> v = valoare;
std::size_t index() const noexcept;

Ce face std::variant

std::variant<int, double, std::string> poate ține fie un int, fie un double, fie un std::string. Știe mereu ce tip conține acum, deci, spre deosebire de union, nu poți citi din greșeală tipul greșit fără să afli.

v.index() returnează poziția tipului curent în listă (0, 1, 2…). Valoarea se citește cu std::get<T>(v) (aruncă std::bad_variant_access dacă tipul nu se potrivește), cu std::get_if sau, cel mai elegant, cu std::visit.

Un variant neinițializat explicit conține o valoare implicită a primului tip.

Ce returnează

Un obiect care conține o valoare de unul dintre tipurile enumerate.

Exemple

Exemplul 1: Același variant, tipuri diferite

#include <print>
#include <string>
#include <variant>

int main() {
    std::variant<int, double, std::string> v;
    std::println("index: {} (int implicit: {})", v.index(), std::get<int>(v));

    v = 3.14;
    std::println("index: {} -> {}", v.index(), std::get<double>(v));

    v = std::string("Timișoara");
    std::println("index: {} -> {}", v.index(), std::get<std::string>(v));
}

Rezultat

index: 0 (int implicit: 0)
index: 1 -> 3.14
index: 2 -> Timișoara

Exemplul 2: Tipul greșit aruncă excepție

#include <print>
#include <string>
#include <variant>

int main() {
    std::variant<int, std::string> rezultat = "eroare de rețea";
    try {
        std::println("{}", std::get<int>(rezultat));
    } catch (const std::bad_variant_access& e) {
        std::println("Nu conține int: {}", e.what());
    }
}

Rezultat

Nu conține int: bad_variant_access

Greșeli frecvente

  • Scrii std::variant<std::string, bool> v = "text";. Înainte de C++20 un literal de tip const char* se transforma în bool; regula a fost corectată, dar e mai sigur să scrii std::string("text").
  • Folosești v.index() cu numere scrise de mână în tot codul. Dacă schimbi ordinea tipurilor, numerele devin greșite; preferă std::visit sau std::holds_alternative.

Comenzi înrudite

  • std::visit: Apelează o funcție cu valoarea curentă dintr-un std::variant, oricare ar fi tipul ei.
  • std::holds_alternative: holds_alternative verifică ce tip are acum un variant; get_if returnează pointer la valoare sau nullptr.
  • std::optional: O valoare care poate lipsi: conține fie un obiect de tip T, fie nimic (std::nullopt).
  • std::any: Container pentru o singură valoare de orice tip; std::any_cast o scoate înapoi cu tipul corect.
  • union: Un tip în care toți membrii împart aceeași zonă de memorie; la un moment dat doar unul dintre ei are o valoare validă.