How to add a key/value pair to a map of variants in cpp

68 Views Asked by At

Wanted to use a std::map<std::string, std::variant<A, B>> my_map where A and B are classes that have similar constructors (take a yaml object as argument), but I cannot figure out how to actually insert a key/value pair into such a map.

a similar minimum example

class A {
    public:
        A(int x): x_(x) { std::cout << "A" << std::endl;}
        int x_;
        void print() {std::cout << 2 * x_ << std::endl;}
};

class B {
    public:
        B (int x): x_(x) {std::cout << "B" << std::endl;}
        int x_;
        void print() {std::cout << 5* x_ << std::endl; }
};

int main()
{
    using var_t = std::variant<A, B>;
    std::map<std::string, var_t> my_map;
    var_t v {std::in_place_type<A>, 3};
    //my_map["a"] = v; // none of these work
    //my_map.emplace("a", v);// 
    my_map.insert({"a", v});
    std::visit([](auto&& var) { var.print();}, my_map["a"]);
}

Results in this

usr/local/include/c++/13.1.0/tuple:2254:9: error: use of deleted function 'std::variant<_Types>::variant() [with _Types = {A, B}]'
 2254 |         second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

I understand that this is due to the fact that the the variant types have ambiguous/non-default constructors so when it tries to build with the key "a" the compiler cannot figure out the correct constructor to use.

What's the correct way to use and add pairs to such a structure ?

1

There are 1 best solutions below

0
Yksisarvinen On

The issue is not with insertion, but with use of operator[]. This operator creates default-initialised object when key is not found in map, and your std::variant has no default constructor*.

Change

    std::visit([](auto&& var) { var.print();}, my_map["a"]);

to

    std::visit([](auto&& var) { var.print();}, my_map.at("a"));

and both of these initialisations will work:

    my_map.emplace("a", v);
    my_map.insert({"a", v});

* Variant only has default constructor if the first alternative can be default constructed. In your case, A has no default constructor, so std::variant will not be default-constructible.