Vấn đề với boost::variant
là nó không phải là mẫu thực variadic chỉ mô phỏng một bằng cách thêm một số thông số mẫu với giá trị mặc định boost::detail::variant::void_
. Để làm việc với nó như với mẫu variadic, bạn cần phải thay đổi nó trước tiên thành gói có các loại có liên quan. Toàn bộ C++ 11 sẵn sàng sử dụng cách tiếp cận để làm những gì bạn muốn có thể trông như sau. Cách tiếp cận này tính đến cả độ phẳng cũng như giả định duy nhất đối với loại kết quả. Phần integer_sequence
sẽ không cần thiết nếu bạn quyết định sử dụng C++ 14:
#include <boost/variant.hpp>
#include <tuple>
#include <type_traits>
template <class T, T... Vs>
struct integer_sequence { };
template <class T, class, class, class = integer_sequence<T>, class = integer_sequence<T, 0>, class = void>
struct make_integer_sequence_impl;
template <class T, T ICV1, T... Res, T... Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, ICV1>, std::integral_constant<T, 0>, integer_sequence<T, Res...>, integer_sequence<T, Pow...>, typename std::enable_if<(ICV1 > 0)>::type>: make_integer_sequence_impl<T, std::integral_constant<T, ICV1/2>, std::integral_constant<T, ICV1%2>, integer_sequence<T, Res...>, integer_sequence<T, Pow..., (Pow + sizeof...(Pow))...>> { };
template <class T, T ICV1, T... Res, T... Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, ICV1>, std::integral_constant<T, 1>, integer_sequence<T, Res...>, integer_sequence<T, Pow...>, void>: make_integer_sequence_impl<T, std::integral_constant<T, ICV1/2>, std::integral_constant<T, ICV1%2>, integer_sequence<T, Pow..., (Res + sizeof...(Pow))...>, integer_sequence<T, Pow..., (Pow + sizeof...(Pow))...>> { };
template <class T, class Res, class Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, 0>, std::integral_constant<T, 0>, Res, Pow, void> {
using type = Res;
};
template <class T, T V>
using make_integer_sequence = typename make_integer_sequence_impl<T, std::integral_constant<T, V/2>, std::integral_constant<T, V%2>>::type;
template <class>
struct is_variant_void {
static constexpr size_t value = 0;
};
template <>
struct is_variant_void<boost::detail::variant::void_> {
static constexpr size_t value = 1;
};
constexpr size_t sum(size_t v){
return v;
}
template <class... Args>
constexpr size_t sum(size_t v, Args... vs){
return v + sum(vs...);
}
template <class Variant>
struct variant_size;
template <class... Ts>
struct variant_size<boost::variant<Ts...>> {
static constexpr size_t value = sizeof...(Ts) - sum(is_variant_void<Ts>::value...);
};
template <class...>
struct pack { };
template <class V, class=make_integer_sequence<size_t, variant_size<V>::value>>
struct variant_to_pack;
template <class... Ts, size_t... Is>
struct variant_to_pack<boost::variant<Ts...>, integer_sequence<size_t, Is...>> {
using type = pack<typename std::tuple_element<Is, std::tuple<Ts...>>::type...>;
};
template <class>
struct unique_opaque { };
template <class... Ts>
struct unique_pack: unique_opaque<Ts>... { };
template <class, class, class = void>
struct unique;
template <class... Res, class T, class... Ts>
struct unique<unique_pack<Res...>, pack<T, Ts...>, typename std::enable_if<std::is_base_of<unique_opaque<T>, unique_pack<Res...>>::value>::type>:
unique<unique_pack<Res...>, pack<Ts...>> { };
template <class... Res, class T, class... Ts>
struct unique<unique_pack<Res...>, pack<T, Ts...>, typename std::enable_if<!std::is_base_of<unique_opaque<T>, unique_pack<Res...>>::value>::type>:
unique<unique_pack<Res..., T>, pack<Ts...>> { };
template <class... Res>
struct unique<unique_pack<Res...>, pack<>, void> {
using type = boost::variant<Res...>;
};
template <class...>
struct flatten;
template <class... Res, class T, class... Rest>
struct flatten<pack<Res...>, T, Rest...>: flatten<pack<Res..., T>, Rest...> { };
template <class... Res, class... Vs, class... Rest>
struct flatten<pack<Res...>, boost::variant<Vs...>, Rest...>: flatten<pack<Res...>, typename variant_to_pack<boost::variant<Vs...>>::type, Rest...> {};
template <class... Res, class... Vs, class... Rest>
struct flatten<pack<Res...>, pack<Vs...>, Rest...>: flatten<pack<Res...>, Vs..., Rest...> { };
template <class Res>
struct flatten<Res>:unique<unique_pack<>, Res> { };
int main() {
boost::variant<int, boost::variant<float, int, boost::variant<char, bool>, bool>> vif;
static_assert(std::is_same<flatten<pack<>, decltype(vif)>::type, boost::variant<int, float, char, bool>>::value, "Test");
}
bạn có thể sử dụng C++ 17/14/11? –
Tôi có thể sử dụng ít nhất C++ 11. – Aleph0
@FrankSimon vui lòng xem câu trả lời đã chỉnh sửa –