I am writing a barebones version of std::variant
for a personal project and learning experience. The visitation strategy that I want to implement is a if...else if
chain, not a constexpr
table of function pointers. The reason is that the latter is notoriously hard for compilers to optimize, and it is easy to produce a benchmark where std::visit
is beaten by a chain of if...else if
.
I am trying to implement it with fold expressions, but I couldn't find a way to return a value when the correct visitor is found. This is what I have so far:
template <typename... Ts>
struct my_variant
{
std::byte _buffer[std::max({sizeof(Ts)...})];
std::size_t _discriminator;
// ...
auto match(auto&&... fs)
{
overload_set matcher(std::forward<Fs>(fs)...);
[&]<std::size_t... Is>(std::index_sequence<Is...>)
{
([&]
{
if (_discriminator == Is)
{
// How to return from here?
matcher(*reinterpret_cast<Ts *>(&_buffer));
}
}(), ...);
}
(std::make_index_sequence_for<Ts...>{});
}
};
My current strategy is to create a std::index_sequence
for all the types in the variant, then fold over the comma operator to make the compiler generate a bunch of if
statements. Since if
is not an expression, I had to wrap it into a lambda expression in order to be able to fold over it. If I try to return, I will return from the lambda itself, and that doesn't propagate to the upper layers.
I could use a buffer to store the result, then return it, but that defeats the purpose as it will prevent RVO.
Is there a way I can write match
non-recursively and still return the visitor's result allowing RVO to take place?