Во-первых, доморощенный вариант C++ 2a-х is_detected
:
#include <utility>
#include <type_traits>
#include <iostream>
#include <tuple>
namespace details {
template<class...>using void_t=void;
template<template<class...>class Z, class=void, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = typename details::can_apply<Z, void, Ts...>::type;
Как это происходит, std :: result_of_t - это признак, который мы хотим проверить.
template<class Sig>
using can_call = can_apply< std::result_of_t, Sig >;
< Теперь can_call Some (Sig, идет, здесь)> является true_type тогда и только тогда выражение, которое вы хотите можно назвать.
Теперь мы пишем какое-то время для компиляции в случае отправки.
template<std::size_t I>
using index_t=std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index_v{};
constexpr inline index_t<0> dispatch_index() { return {}; }
template<class B0, class...Bs,
std::enable_if_t<B0::value, int> =0
>
constexpr index_t<0> dispatch_index(B0, Bs...) { return {}; }
template<class B0, class...Bs,
std::enable_if_t<!B0::value, int> =0
>
constexpr auto dispatch_index(B0, Bs...) {
return index_v< 1 + dispatch_index(Bs{}...) >;
}
template<class...Bs>
auto dispatch(Bs...) {
using I = decltype(dispatch_index(Bs{}...));
return [](auto&&...args){
return std::get<I::value>(std::make_tuple(decltype(args)(args)..., [](auto&&...){}));
};
}
Отправка (SomeBools ...) возвращает лямбда. Первый из SomeBools, который является правдой времени компиляции (имеет значение ::, которое вычисляет значение true в булевом контексте) определяет, что делает возвращенная лямбда. Позвоните, что индекс отправки.
Он возвращает аргумент dispatch_index'd для следующего вызова и пустую лямбда, если это один конец конца списка.
template <class FV, class FI, class... Args /*, Some template metaprog here */>
void apply_on_validity(FV&& valid_f, FI&& invalid_f, Args&&... args)
{
dispatch(
can_call<FV(Args...)>{},
can_call<FI(Args...)>{}
)(
[&](auto&& valid_f, auto&&)->decltype(auto) {
return decltype(valid_f)(valid_f)(std::forward<Args>(args)...);
},
[&](auto&&, auto&& invalid_f)->decltype(auto) {
return decltype(invalid_f)(valid_f)(std::forward<Args>(args)...);
}
)(
valid_f, invalid_f
);
}
и сделано, live example.
Мы могли бы сделать это общее для включения новой версии. Первый index_over:
template<class=void, std::size_t...Is >
auto index_over(std::index_sequence<Is...>){
return [](auto&&f)->decltype(auto){
return decltype(f)(f)(std::integral_constant<std::size_t, Is>{}...);
};
}
template<std::size_t N>
auto index_over(std::integral_constant<std::size_t, N> ={}){
return index_over(std::make_index_sequence<N>{});
}
Тогда auto_dispatch:
template<class...Fs>
auto auto_dispatch(Fs&&... fs) {
auto indexer = index_over<sizeof...(fs)>();
auto helper = [&](auto I)->decltype(auto){
return std::get<decltype(I)::value>(std::forward_as_tuple(decltype(fs)(fs)...));
};
return indexer
(
[helper](auto...Is){
auto fs_tuple = std::forward_as_tuple(helper(Is)...);
return [fs_tuple](auto&&...args) {
auto dispatcher = dispatch(can_call<Fs(decltype(args)...)>{}...);
auto&& f0 = dispatcher(std::get<decltype(Is)::value>(fs_tuple)...);
std::forward<decltype(f0)>(f0)(decltype(args)(args)...);
};
}
);
}
с тестовым кодом:
auto a = [](int x){ std::cout << x << "\n"; };
auto b = [](std::string y){ std::cout << y << "\n"; };
struct Foo {};
auto c = [](Foo){ std::cout << "Foo\n"; };
int main() {
auto_dispatch(a, c)(7);
auto_dispatch(a, c)(Foo{});
auto_dispatch(a, b, c)(Foo{});
auto_dispatch(a, b, c)("hello world");
}
Live example
Вы можете рассмотреть глядя на [ 'void_t'] (HTTP: // en.cppreference.com/w/cpp/types/void_t) и имеющий SFINAE, основанный на выражении 'void_t'. – vsoftco
, но вы хотите это, когда 'valid_f' и' invalid_f' являются аргументами 'apply_on_validity()'? Не с двумя известными и фиксированными функциями? – max66
Не могу * подождать * для 'if constexpr' ... – Barry