Info
-
Did you know that the underlying visit implementation of std::visit has changed since GCC12+, Clang15+?
Example
template<class... Ts>
struct inc {
std::variant<Ts...> v{};
auto operator++() {
std::visit([&](auto& v) { ++v; }, v);
}
template<class T>
operator T() const {
T result{};
std::visit(
overloaded{
[&](T v) { result = v; },
[](auto&&) {}
}
, v);
return result;
}
};
auto visit(std::int16_t size, std::int32_t iterations) {
auto it = inc<std::int8_t, std::int16_t, std::int32_t>{size};
for (auto i = 0; i < iterations; ++i, ++it)
;
return std::int16_t(it);
}
Puzzle
-
**Can you implement different versions of std::visit dispatching and compare its performance?
- generated switch case
- jump table
- fold expression
- if/else
- generted goto
- ...
constexpr auto visit(auto&& f, auto&& v); // TODO
Solutions
template<class F, class V, class R, std::size_t N, std::size_t Size>
constexpr auto visit_impl([[maybe_unused]] F&& f, [[maybe_unused]] V&& v) -> R {
if constexpr (N < Size) {
switch (v.index()) {
default:
return visit_impl<F, V, R, N+1, Size>(std::forward<F>(f), std::forward<V>(v));
case N:
return std::forward<F>(f)(std::get<N>(std::forward<V>(v)));
}
} else {
__builtin_unreachable();
}
}
template<class F, class V, template<class...> class T, class... Ts>
auto result_type(T<Ts...>&&) ->
std::common_type_t<decltype(std::declval<F>()(std::get<Ts>(std::declval<V>())))...>;
template<class F, class V, template<class...> class T, class... Ts>
auto result_type(T<std::monostate, Ts...>&&) ->
std::common_type_t<decltype(std::declval<F>()(std::get<Ts>(std::declval<V>())))...>;
template<class F, class V>
constexpr decltype(auto) visit(F&& f, V&& v) {
using variant_t = std::remove_const_t<std::remove_reference_t<V>>;
constexpr auto size = std::variant_size_v<variant_t>;
static_assert(size > 0, "Empty variant is not supported!");
using result_t = decltype(result_type<F, V>(std::declval<variant_t>()));
return visit_impl<F, V, result_t, 0u, size>(std::forward<F>(f), std::forward<V>(v));
}