c++std-variant

Runtime type info for accessing std::variant data


How to access std::variant using std::get by specifying the type info during runtime. Below is a non working code that I tried

#include <iostream>
#include <string>
#include <variant>

class A {
public:
    A(const int a) {
        va = a;
        // below line doesn't compile
        // variant_type = int;
    }

    A(const std::string& a) {
        va = a;
        // below line doesn't compile
        // variant_type = std::string;
    }

    // type variant_type;
    // above line doesn't compile
    std::variant<int, std::string> va{};
};

int main() {
    std::variant<int, std::string> va{"abcd"};
    std::cout << std::get<1>(va) << std::endl;

    va = 23;
    std::cout << std::get<0>(va) << std::endl;

    const A a_obj{1};
    // below line doesn't compile
    // std::get<a_obj.type>(a_obj);

    const A b_obj{"abc"};
    // below line doesn't compile
    // std::get<b_obj.type>(b_obj);
}

Solution

  • Types are not values that you can store in a data member.

    In order to access the std::variant data based on its type, you have several options:

    1. Use std::get with a specific type. If you know it at compile-time you can use it plainly. If you want to check the type at runtime before the call to std::get, you can use std::holds_alternative.
    2. Use std::visit with a generic lambda. Will be resolved in runtime.
    3. Use std::visit with a lambda per variant type. This is useful if you need separate logic per type (will be also resolved in runtime).
    #include <variant>
    #include <string>
    #include <iostream>
    
    class A {
    public:
        A(const int a) { va = a; }
        A(const std::string& a) { va = a; }
        std::variant<int, std::string> va{};
    };
    
    void A_visitor_generic_lambda(A const& a) {
        std::visit([](auto&& arg) { std::cout << arg << std::endl; }, a.va);
    }
    
    template<class... Ts>
    struct overloaded : Ts... { using Ts::operator()...; };
    // explicit deduction guide (not needed as of C++20)
    template<class... Ts>
    overloaded(Ts...)->overloaded<Ts...>;
    
    void A_visitor_separate_lambdas(A const& a) {
        std::visit(overloaded{
                     [](int arg) { std::cout << arg << std::endl; },
                     [](std::string const& arg) { std::cout << arg << std::endl; } },
                   a.va);
    }
    
    int main() {
         const A a_obj{ 1 };
         const A b_obj{ "abc" };
    
        // 1. Use `std::get` with a specific type, after checking the type with `std::holds_alternative`:
        if (std::holds_alternative<int>(a_obj.va)) {
            std::cout << std::get<int>(a_obj.va) << std::endl;
        }
        if (std::holds_alternative<std::string>(b_obj.va)) {
            std::cout << std::get<std::string>(b_obj.va) << std::endl;
        }
    
        // 2. Use `std::visit` with a generic labmda:
        A_visitor_generic_lambda(a_obj);
        A_visitor_generic_lambda(b_obj);
    
        // 3. Use `std::visit` with a separate overload lambdas:
        A_visitor_separate_lambdas(a_obj);
        A_visitor_separate_lambdas(b_obj);
    }
    

    Output:

    1
    abc
    1
    abc
    1
    abc
    

    Live demo