variant with base class access

Motivation

Have direct access to common base class of std::variant.

    struct Base{
        int pos;
    };
    struct A : Base{
        int a = 10;
    };
    struct B : Base{
        int b = 20;
    };
    
    std::variant<A,B> var;
    Base& base = std::get<Base>(var);    // can't do this
    // can do this, but this will cost you.
    Base& base = std::visit([](Base& base) -> Base& { 
        return base;
    }, var);

Final solution

    variant_w_base<Base, std::variant<A,B>> var;
    Base& base = *var.base();
    Base& base = var.get<Base>();

source code

Step-by-step

To allow base class access, we store pointer to base.

    template<class Base, class Variant>
    class variant_w_base{
        Base* m_base;
        Variant m_variant;

        void update_base(){
            m_base = std::visit([](auto&& arg) -> Base* {
                using Arg = std::decay_t<decltype(arg)>;
                if constexpr (std::is_same_v<Arg, std::monostate>){
                    return nullptr;
                } else {
                    return static_cast<Base*>(&arg);
                }
            }, m_variant);
        }
    }

Each time value changed, copied, moved we update base class pointer.

Interface similar to std::variant, but all free functions are members.

Performance measurements

Accessing 100’000 element’s base class data. (aka dumb linear search). 100 repeats:

std::variant   21ms
variant_w_base 2ms

Benchmark


P.S. It is also possible to use it as virtual class local storage, if you know all posible classes beforehand:

struct Interface{
    virtual int get() = 0;
};
struct A : Interface{
    virtual int get() override{ return 1; }
};
struct B : Interface{
    virtual int get() override{ return 2; }
};

variant_w_base<Interface, std::variant<std::monostate, A, B>> var;

var = A();
Interface* inteface = var.base();
inteface->get();

Comments