std::variant of classes with same base class

std::variant overhead
std::variant performance
std::variant c++11
variant std get
std variant apply_visitor
std::any vs std::variant
std::visit return value
boost::variant

I'm not fully comprehending variants' usage, maybe someone can clear up what I'm doing wrong (possibly my approach).

Let the variant object be _Types of two classes, both inheriting the same class.

class base
{
public:
    int foo;
};
class a: public base
{
    int bar;
};
class b: public base
{
    float bar;
};

byte variant_id = 0; // 0 for class A, 1 for class B
std::variant< base, a, b > variant;

Here's how I use the variant:

void func( )
{
    auto& _variant = std::get< base >( variant ); // ideally would be = variant_id ? std::get< b >( variant ) : std::get< a >( variant )
    _variant.foo = 20;

    if ( variant_id == 1 )
    {
        auto& variant_ = std::get< b >( variant );
        variant_.bar = 20.f;
    }
    else
    {
        auto& variant_ = std::get< a >( variant );
        variant_.bar = 20;
    }

Maybe a union is more effective?

union
{
    a _a;
    b _b;
} variant;
byte variant_id = 0;

Try not to query the variant for what type it holds. If you do, your code is essentially equivalent to a bunch of dynamic casts in an if chain, a code smell.

Instead, let the variant dispatch for you. And if you want to access the common base of a and b, you do not need a base member in that variant.

Use a visitor

std::variant< a, b > var;
void func( )
{
   std::visit([](auto&& v) {
     v.foo = 20; // Both `a` and `b` have a foo, this is well formed.
     v.bar = 20; // Both have a `bar` that can be assigned a 20. This is well formed too
   }, var);
}

std::variant and accessing BaseClass (std::get<BaseClass , If so, the only thing that prevent getting Base class is std::variant's sanity checker. Which can be enhanced to recognize Base types (runtime checks disabled on In every if in std::visit it calls the same function using the same address as  If so, the only thing that prevent getting Base class is std::variant's sanity checker. Which can be enhanced to recognize Base types (runtime checks disabled on NDEBUG anyway). Nonetheless, std::get is virtually no-op compared to std::visit.


What std::variant does for you is to track what the current (last assigned) type is, and complain when you try to get a different type. So instead of tracking variant_id you should be using variant.index().

Also I believe that your first extraction of base will actually fail if the type assigned is not of the base type. Assuming your objects will always be of type a or b (and never base), you should drop the base type from the variant type constructor.

I'm assuming here that you are not creating classes base, a and b yourself (and can't touch them), and thus the virtual method approach isn't viable.

A vector of pointers to different derived classes of the same base class, You can specify base class with virtual function Update and derived classes std​::vector holds its elements by value so if you store subclasses you are slicing  Everything You Need to Know About std::variant from C++17 Around the time C++17 was being standardized I saw magical terms like “discriminated union”, “type-safe union” or “sum type” floating around.


Koehler has given a good answer for some technical errors in your usage, but I feel variant is the wrong tool for the job here.

Typically, you would use an std::variant for unrelated datatypes. Is there a reason to use variant here? Since you are only holding sub-classes of base, you'd usually opt for a std::unique_ptr<base> or std::shared_ptr<base> (depending on the requirements) and be done with it.

The only reason I'd see to use a variant of sub-classes would be to ensure they can be stored contiguously to reduce memory/indirection costs. And even then, I'd use it through the base class interface, like so:

base& getBase(std::variant<a, b>& v)
{
   // the conditional here might be omitted in the generated
   // code since the branches might be identical
   return v.index() == 0 ? std::get<a>(v) : std::get<b>(v);
}

// use like
base& b = getBase(variant);
b.func(20); 

std::variant, An example of an inheritance hierarchy with virtual base classes is the iostreams hierarchy of the standard library: std::istream and std::ostream  If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list, cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf.


Variant knows which type it stores. Union expects you are keeping the track of that type externally. If you try to access the wrong item in a variant, you get an exception or nullptr, for the Union it is merely undefined behavior. (ref)

Derived classes, Sum types are objects that can vary their type dynamically. A std::variant is similar to a union: it allocates a fixed portion of memory and pack Ts . This is achieved by expanding Ts in the inheritance list: struct Visitor: Ts.. You can't cast these types. map<char*,Base*> and map<char*,Derived*> are as different as string and float. Simplest thing to do is within test() simply populate a map<char*,Base*> and call call with that.


std::variant Doesn't Let Me Sleep, 5 One interesting effect of distinguishing the VariantChoice base classes of a indicating that a class cannot directly inherit from the same base class (in this be addressed by wrapping references in a class such as std::reference_wrapper. The child class will define only part of the data and function member from the base class. Ideally I would like to have a common interface between the two child classes. however, since function get_value1 from one class returns integer, the other get_value2 from another class returns double, I have to give them a different function name.


C++ Templates: The Complete Guide, How to implement runtime polymorphism with C++17, std::variant and Instead of a pointer to a base class, std::variant can store all “derived” classes. Duck typing: while virtual functions need to have the same signatures,  You should store pointer to object of Base class in the vector: vector<Base*> By storing a pointer to Base class there would be no slicing and you can achieve the desired polymorphic behavior as well. Since you ask for a C++ish way of doing this, the right approach is to use a suitable Smart pointer instead of storing a raw pointer in the vector.


Runtime Polymorphism with std::variant and , Inheritance and Virtual Table are often used to create interface in C++ polymorphic class that allows developers to create a hierarchy between classes, using "is-a" relationships. So, an all designated solution might be to use std::variant. Variant is not allowed to allocate additional (dynamic) memory. A variant is not permitted to hold references, arrays, or the type void. Empty variants are also ill-formed (std:: variant < std:: monostate > can be used instead). A variant is permitted to hold the same type more than once, and to hold differently cv-qualified versions of the