Я нахожу этот тип кода интересным для написания.
any_visitor<types...>
- это функциональный объект, который посещает набор типов.
Вы вызываете его с любым, за которым следует объект функции. Затем он вызывает функциональный объект в зависимости от того, какой из types...
находится в any
.
Итак, вы делаете any_vistor<int, double>{}( something, [](auto&& x) { /* some code */ } )
.
Если ни один из types...
не находится в any
, он вызывает функциональный объект с std::any
для вас, чтобы иметь дело с дополнительным регистром.
Мы также можем написать вариант, который вместо передачи std::any
функтору генерирует или возвращает false или что-то в этом роде.
template<class...Ts>
struct any_visitor;
template<>
struct any_visitor<> {
template<class F>
decltype(auto) operator()( std::any& a, F&& f ) const {
return std::forward<F>(f)(a);
}
};
template<class...Ts>
struct any_visitor {
private:
struct accum {
std::size_t x = 0;
friend accum operator+( accum lhs, accum rhs ) {
if (lhs.x || rhs.x) return {lhs.x+1};
else return {};
}
};
public:
template<class Any, class F>
void operator()(Any&& any, F&& f) const {
// sizeof...(Ts) none in the list
// otherwise, index of which any is in the list
std::size_t which = sizeof...(Ts) - (accum{} + ... + accum{ any.type() == typeid(Ts) }).x;
using table_entry = void(*)(Any&&, F&&);
static const table_entry table[] = {
+[](Any&& any, F&& f) {
std::forward<F>(f)( std::any_cast<Ts>( std::forward<Any>(any) ) );
}...,
+[](Any&& any, F&& f) {
std::forward<F>(f)( std::forward<Any>(any) );
}
};
table[which]( std::forward<Any>(any), std::forward<F>(f) );
}
};
template<class...Fs>
struct overloaded:Fs... {
using Fs::operator()...;
};
template<class...Fs>
overloaded(Fs&&...)->overloaded<std::decay_t<Fs>...>;
Я также включил overloaded
, что облегчает отправку. Если вы хотите обрабатывать все типы одинаково, за исключением случая ошибки, вы можете сделать:
overloaded{
[](auto const& x){ std::cout << x << "\n"; },
[](std::any const&){ std::cout << "Unknown type\n"; }
}
и передать это как функциональный объект any_visitor
.
Вот тестовый код:
std::any foo=7;
std::any bar=3.14;
auto visitor = overloaded{
[](int x){std::cout << x << "\n";},
[](auto&&){std::cout << "Unknown\n";}
};
any_visitor<int>{}( foo, visitor );
any_visitor<int>{}( bar, visitor );
который выводит:
7
Unknown
Живой пример .
В реализации, этот код использует таблицу диспетчеризации (вроде как в vtable) для отображения индекса типа, хранящегося в объекте any, для которого вызывается перегрузка объекта функции.
Еще одним подходом было бы написать:
template<class...Ts>
std::optional<std::variant<Ts...>> to_variant( std::any );
, который преобразует std::any
в вариант, если его типы совпадают. Затем используйте обычную машину для посещения на std::variant
вместо того, чтобы кататься на собственной.