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>();
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
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