Giải pháp của tôi cho nhiều lần truy cập. Nhờ Jarod42 cho tôi thấy con đường với sự truy cập biến thể duy nhất.
Live Demo
Vấn đề chính là để tạo ra chéo sản phẩm của tất cả các cuộc gọi có thể để một bộ tình trạng quá tải. Câu trả lời này không giải quyết được vấn đề về việc chuyển đổi chung loại trả lại, tôi chỉ thực hiện một chuyên môn đặc biệt của std::common_type
(Tôi nghĩ điều này là khó khăn cho phù hợp với nhu cầu của tôi, nhưng cảm thấy tự do để đóng góp!).
Xem các bài kiểm tra thời gian biên dịch ở cuối để hiểu từng hàm meta mẫu.
Hãy đề nghị đơn giản hóa (std::index_sequence
ai?)
#include <variant>
#include <iostream>
#include <type_traits>
// ========= Library code ========= //
// --- Operations on types --- //
template<class... Ts>
struct Types; // used to "box" types together
// Lisp-like terminology
template<class Head, class Tail>
struct Cons_types;
template<class Head, class... Ts>
struct Cons_types<Head,Types<Ts...>> {
using type = Types<Head,Ts...>;
};
template<class... _Types>
struct Cat_types;
template<class _Types, class... Other_types>
struct Cat_types<_Types,Other_types...> {
using type = typename Cat_types<_Types, typename Cat_types<Other_types...>::type>::type;
};
template<class... T0s, class... T1s>
struct Cat_types< Types<T0s...> , Types<T1s...> > {
using type = Types< T0s..., T1s... >;
};
template<class... T0s>
struct Cat_types< Types<T0s...> > {
using type = Types<T0s...>;
};
template<class Head, class Types_of_types>
struct Cons_each_types;
template<class Head, class... Ts>
struct Cons_each_types<Head,Types<Ts...>> {
using type = Types< typename Cons_types<Head,Ts>::type... >;
};
template<class Head>
struct Cons_each_types<Head,Types<>> {
using type = Types< Types<Head> >;
};
template<class _Types>
struct Cross_product;
template<class... Ts, class... Other_types>
struct Cross_product< Types< Types<Ts...>, Other_types... > > {
using type = typename Cat_types< typename Cons_each_types<Ts,typename Cross_product<Types<Other_types...>>::type>::type...>::type;
};
template<>
struct Cross_product<Types<>> {
using type = Types<>;
};
// --- Operations on return types --- //
template<class Func, class _Types>
struct Common_return_type;
template<class Func, class... Args0, class... Other_types>
struct Common_return_type<Func, Types< Types<Args0...>, Other_types... >> {
using type =
std::common_type_t<
std::result_of_t<Func(Args0...)>, // C++14, to be replaced by std::invoke_result_t in C++17
typename Common_return_type<Func,Types<Other_types...>>::type
>;
};
template<class Func, class... Args0>
struct Common_return_type<Func, Types< Types<Args0...> >> {
using type = std::result_of_t<Func(Args0...)>;
};
// --- Operations on variants --- //
template<class... Vars>
struct Vars_to_types;
template<class... Ts, class... Vars>
struct Vars_to_types<std::variant<Ts...>,Vars...> {
using type = typename Cons_types< Types<Ts...> , typename Vars_to_types<Vars...>::type >::type;
};
template<>
struct Vars_to_types<> {
using type = Types<>;
};
template<class Func, class... Vars>
// requires Func is callable
// requires Args are std::variants
struct Common_return_type_of_variant_args {
using Variant_args_types = typename Vars_to_types<Vars...>::type;
using All_args_possibilities = typename Cross_product<Variant_args_types>::type;
using type = typename Common_return_type<Func,All_args_possibilities>::type;
};
template <typename Func, class... Args>
// requires Args are std::variants
decltype(auto)
visit_ext(Func&& f, Args... args) {
using Res_type = typename Common_return_type_of_variant_args<Func,Args...>::type;
return std::visit(
[&](auto&&... e)
-> Res_type
{
return f(std::forward<decltype(e)>(e)...);
},
std::forward<Args>(args)...);
}
// ========= Application code ========= //
struct A {
int i;
};
struct B {
int j;
};
// This part is not generic but is enough
namespace std {
template<>
struct common_type<A,B> {
using type = std::variant<A,B>;
};
template<>
struct common_type<B,A> {
using type = std::variant<A,B>;
};
template<>
struct common_type<A,std::variant<A,B>> {
using type = std::variant<A,B>;
};
template<>
struct common_type<std::variant<A,B>,A> {
using type = std::variant<A,B>;
};
template<>
struct common_type<B,std::variant<A,B>> {
using type = std::variant<A,B>;
};
template<>
struct common_type<std::variant<A,B>,B> {
using type = std::variant<A,B>;
};
}
struct Functor {
auto
operator()(A a0,A a1) -> A {
return {a0.i+2*a1.i};
}
auto
operator()(A a0,B b1) -> A {
return {3*a0.i+4*b1.j};
}
auto
operator()(B b0,A a1) -> B {
return {5*b0.j+6*a1.i};
}
auto
operator()(B b0,B b1) -> B {
return {7*b0.j+8*b1.j};
}
};
// ========= Tests and final visit call ========= //
int main() {
std::variant<A,B> var0;
std::variant<A,B> var1;
using Variant_args_types = typename Vars_to_types<decltype(var0),decltype(var1)>::type;
static_assert(
std::is_same_v<
Types< Types<A,B>, Types<A,B> >,
Variant_args_types
>
);
using Cons_A_Nothing = typename Cons_each_types<A, Types<> >::type;
static_assert(
std::is_same_v<
Types< Types<A> >,
Cons_A_Nothing
>
);
using Cons_A_AB = typename Cons_each_types<A, Types<Types<A>,Types<B>> >::type;
using Cons_B_AB = typename Cons_each_types<B, Types<Types<A>,Types<B>> >::type;
static_assert(
std::is_same_v<
Types< Types<A,A>, Types<A,B> >,
Cons_A_AB
>
);
using Cat_types_A = typename Cat_types<Cons_A_Nothing>::type;
static_assert(
std::is_same_v<
Types< Types<A> >,
Cat_types_A
>
);
using Cat_types_AA_AB_BA_BB = typename Cat_types<Cons_A_AB,Cons_B_AB>::type;
static_assert(
std::is_same_v<
Types< Types<A,A>, Types<A,B>, Types<B,A>, Types<B,B> >,
Cat_types_AA_AB_BA_BB
>
);
using Depth_x1_1_cross_product = typename Cross_product<Types<Types<A>>>::type;
static_assert(
std::is_same_v<
Types< Types<A> >,
Depth_x1_1_cross_product
>
);
using Depth_x2_1_1_cross_product = typename Cross_product<Types<Types<A>,Types<B>>>::type;
static_assert(
std::is_same_v<
Types< Types<A,B> >,
Depth_x2_1_1_cross_product
>
);
using All_args_possibilities = typename Cross_product<Variant_args_types>::type;
static_assert(
std::is_same_v<
Types< Types<A,A>, Types<A,B>, Types<B,A>, Types<B,B> >,
All_args_possibilities
>
);
using Functor_AorB_AorB_common_return_type = typename Common_return_type<Functor,All_args_possibilities>::type;
static_assert(
std::is_same_v<
std::variant<A,B>,
Functor_AorB_AorB_common_return_type
>
);
using Functor_varAB_varAB_common_return_type = typename Common_return_type_of_variant_args<Functor,decltype(var0),decltype(var1)>::type;
static_assert(
std::is_same_v<
std::variant<A,B>,
Functor_varAB_varAB_common_return_type
>
);
var0 = A{42};
var1 = A{43};
auto res0 = visit_ext(Functor(), var0,var1);
std::cout << "res0 = " << std::get<A>(res0).i << "\n";
var0 = A{42};
var1 = B{43};
auto res1 = visit_ext(Functor(), var0,var1);
std::cout << "res1 = " << std::get<A>(res1).i << "\n";
var0 = B{42};
var1 = A{43};
auto res2 = visit_ext(Functor(), var0,var1);
std::cout << "res2 = " << std::get<B>(res2).j << "\n";
var0 = B{42};
var1 = B{43};
auto res3 = visit_ext(Functor(), var0,var1);
std::cout << "res3 = " << std::get<B>(res3).j << "\n";
}
@ Jarod42 Có bạn là đúng, đây không phải là vấn đề. Tôi đã không rõ ràng vì vậy tôi đã chỉnh sửa câu hỏi: phần thực sự mất tích thực sự là quá tải thiết lập loại trả lại phổ biến –