I am trying to iterate over filtered (by type) std::vector<std::variant<T...>>
.
Problem I hit is that the std::get
is overloaded function(and has many implicit template args I need to type out) so I would need to cast it to something specific.
This seems ugly and hard so I tried to hack together holds_alternative
and get
into a functor that returns a view of filter
( for holds_alternative
) and transform
( for get
) views.
That does not work.
Questions:
- can I do what I want without providing my structs and without ugly casts needed to specify
std::get
overload, ugly need to specify all template arguments tostd::holds_alternative
,std::get
? - if answer to previous quesion is no: why does my attempt does not work
note:
my code does not attempt to handle properly const
and &&
like std::get
does, I am fine with it working only on const
l-values.
My code:
#include <algorithm>
#include <iostream>
#include <ranges>
#include <variant>
#include <vector>
struct Red {
int x = 0;
};
struct Green {
int x = 1;
};
struct Blue {
int x = 2;
};
using Color = std::variant<Red, Green, Blue>;
template <typename T> struct engaged_t {
template <typename... Ts>
constexpr bool operator()(const std::variant<Ts...> &variant) {
return std::holds_alternative<T>(variant);
}
};
template <typename T> inline constexpr auto engaged = engaged_t<T>{};
template <typename T> struct variant_get_t {
template <typename... Ts>
constexpr decltype(auto) operator()(std::variant<Ts...> &variant) {
return std::get<T>(variant);
}
};
template <typename T> inline constexpr auto variant_get = variant_get_t<T>{};
template <typename T> struct variant_filter_t {};
template <typename T>
inline constexpr auto variant_filter = variant_filter_t<T>{};
template <typename R, typename T>
decltype(auto) operator|(const R &r, variant_filter_t<T>) {
return r | std::views::filter(engaged<T>) |
std::views::transform(variant_get<T>);
}
int main() {
std::vector<Color> colors{Red{}, Green{}, Blue{}, Green{}};
// does not compile
// for (const auto green: colors | variant_filter<Green>){
// std::cout << std::get<Green>(green).x << std::endl;
// }
// kaboom if we try to do anything, just to show we can pipe colors to
// variant_get
for (const auto bad_green : colors |
std::views::transform(variant_get<Green>) |
std::views::take(0)) {
}
// we can also pipe colors to engaged
for (const auto bluev : colors | std::views::filter(engaged<Blue>)) {
std::cout << std::get<Blue>(bluev).x << std::endl;
}
// and if we manually doe what variant_filter should_do that also seems to
// work
for (const auto blue : colors | std::views::filter(engaged<Blue>) |
std::views::transform(variant_get<Blue>)) {
std::cout << blue.x << std::endl;
}
}