c++polymorphismvariant

Polymorphism in std::variant when all template types are derived from the same base class


I want to store objects of different types in a std::set and rely on std::set's sorting to later access them by some key variable which is present in every type. To store different types I am using variant.

In a first step I created the same base class but for writing the comparator I am struggling how to access the base. I have something like the following to cast to the shared base class from a variant object:

#include <variant>
#include <string>
struct Base {
  int i;
};
struct A : public Base {
  int j;
};
struct B : public Base {
  std::string k;
};

int main() {
  std::variant<A, B> v(A{{1}, 2});

  // how to get the following without UB and statically (Base offset
  // within variant should be known at compile time)
  Base &b{*reinterpret_cast<Base *>(&std::get<A>(v))};
}

The code above will fail (throw exception) when v does not contain an A type object. But apart from that it be theoretically possible to cast such a variant to the base Base because within the variant the data (where the different A or B objects are placed) is aligned from what I have read.

What is a clean way to do polymorphism for a variant in such a case where all used types share the same base class? I don't want to use visit because it is dynamically dispatching, doing runtime case distinction on the variant's actual type index variable. I feel it should be possible to do this without such case distinction but I fear std::variant does not support this.

Also I am wondering if it is UB if I do the cast manually. Maybe I can also somehow get the offset of the data storage at compile time and add it to the address of a variant object?


Solution

  • You might use std::visit as any std::variant:

    Base& getBase(std::variant<A, B>& a_or_b)
    {
        return std::visit([](auto& elem) -> Base& { return elem; }, a_or_b);
    }