Split tipe std :: varian yang diberikan oleh kriteria yang diberikan

20

Bagaimana dengan jenis varian yang diberikan

using V = std::variant<bool, char, std::string, int, float, double, std::vector<int>>;

mendeklarasikan dua tipe varian

using V1 = std::variant<bool, char, int, float, double>;
using V2 = std::variant<std::string, std::vector<int>>;

dari mana V1menyertakan semua jenis aritmatika dari Vdan V2termasuk semua jenis non-aritmatika dari V?

V bisa menjadi parameter kelas templat, misalnya:

template <class V>
struct TheAnswer
{
    using V1 = ?;
    using V2 = ?;
};

secara umum kriteria dapat berupa constexprvariabel seperti ini:

template <class T>
constexpr bool filter;
Alexey Starinsky
sumber

Jawaban:

6

Jika karena alasan apa pun Anda tidak ingin menggunakan jawaban singkat dan masuk akal Barry, berikut adalah salah satu yang tidak (terima kasih @ xskxzr untuk menghapus spesialisasi "bootstrap" yang canggung, dan ke @ max66 untuk memperingatkan saya terhadap kasing varian kosong) :

namespace detail {
    template <class V>
    struct convert_empty_variant {
        using type = V;
    };

    template <>
    struct convert_empty_variant<std::variant<>> {
        using type = std::variant<std::monostate>;
    };

    template <class V>
    using convert_empty_variant_t = typename convert_empty_variant<V>::type;

    template <class V1, class V2, template <class> class Predicate, class V>
    struct split_variant;

    template <class V1, class V2, template <class> class Predicate>
    struct split_variant<V1, V2, Predicate, std::variant<>> {
        using matching = convert_empty_variant_t<V1>;
        using non_matching = convert_empty_variant_t<V2>;
    };

    template <class... V1s, class... V2s, template <class> class Predicate, class Head, class... Tail>
    struct split_variant<std::variant<V1s...>, std::variant<V2s...>, Predicate, std::variant<Head, Tail...>>
    : std::conditional_t<
        Predicate<Head>::value,
        split_variant<std::variant<V1s..., Head>, std::variant<V2s...>, Predicate, std::variant<Tail...>>,
        split_variant<std::variant<V1s...>, std::variant<V2s..., Head>, Predicate, std::variant<Tail...>>
    > { };
}

template <class V, template <class> class Predicate>
using split_variant = detail::split_variant<std::variant<>, std::variant<>, Predicate, V>;

Lihat langsung di Wandbox

Quentin
sumber
Mungkin Anda bisa membongkar Types...dalamnya std::variantsecara langsung, seperti ini ?
xskxzr
Maaf, tapi ... sejauh yang saya tahu, yang kosong std::varianttidak terbentuk dengan baik.
Maks66
@ max66 Tampaknya hanya instantiating yang bentuknya jelek std::variant<>, jadi saya jelas. Saya akan men-tweak itu V1dan V2jatuh kembali ke std::variant<std::monostate>olah.
Quentin
Ah ... buruk bentuknya hanya jika dipakai ... OK; tampaknya masuk akal bagi saya.
Maks66
14

Dengan Boost.Mp11 , ini adalah one-liner pendek (seperti biasa):

using V1 = mp_filter<std::is_arithmetic, V>;
using V2 = mp_remove_if<V, std::is_arithmetic>;

Anda juga bisa menggunakan:

using V1 = mp_copy_if<V, std::is_arithmetic>;

untuk membuat keduanya lebih simetris.


Kalau tidak,

using P = mp_partition<V, std::is_arithmetic>;
using V1 = mp_first<P>;
using V2 = mp_second<P>;
Barry
sumber
Gagasan apa ini mp_filter?
Alexey Starinsky
@AlexeyStarinsky Saya tidak mengerti pertanyaannya - apa maksud Anda, ide apa?
Barry
3
@AlexeyStarinsky Baca dokumentasi, itu juga tautan ke beberapa posting yang ditulis Peter, cukup informatif.
Barry
4
@ MaximEgorushkin Ini adalah imo metaprogramming library terbaik. Saya punya banyak jawaban di sini yang dimulai dengan "Dengan Boost.Mp11, ini adalah kalimat singkat"
Barry
1
@Barry Saya membaca dokumen sekarang dan terlihat jauh lebih baik daripada boost.MPL.
Maxim Egorushkin
2

EDIT Mengingat bahwa varian kosong ( std::variant<>) sedang terbentuk (menurut cppreference ) dan yang seharusnya digunakan std::variant<std::monostate>, saya telah memodifikasi jawabannya (menambahkan tuple2variant()spesialisasi untuk tuple kosong) untuk mendukung kasus ketika daftar jenis V1atau V2kosong.


Ini sedikit decltype()mengigau tapi ... jika Anda mendeklarasikan beberapa filter helper berfungsi sebagai berikut

template <bool B, typename T>
constexpr std::enable_if_t<B == std::is_arithmetic_v<T>, std::tuple<T>>
   filterArithm ();

template <bool B, typename T>
constexpr std::enable_if_t<B != std::is_arithmetic_v<T>, std::tuple<>>
   filterArithm ();

dan fungsi tuple to varian (dengan spesialisasi untuk tuple kosong, untuk menghindari yang kosong std::variant)

std::variant<std::monostate> tuple2variant (std::tuple<> const &);

template <typename ... Ts>
std::variant<Ts...> tuple2variant (std::tuple<Ts...> const &);

kelas Anda hanya (?) menjadi

template <typename ... Ts>
struct TheAnswer<std::variant<Ts...>>
 {
   using V1 = decltype(tuple2variant(std::declval<
                 decltype(std::tuple_cat( filterArithm<true, Ts>()... ))>()));
   using V2 = decltype(tuple2variant(std::declval<
                 decltype(std::tuple_cat( filterArithm<false, Ts>()... ))>()));
 };

Jika Anda menginginkan sesuatu yang lebih umum (jika Anda ingin meneruskan std::arithmeticsebagai parameter templat), Anda dapat memodifikasi filterArithm()fungsi yang melewati parameter filter templat-templat F(diganti namanya filterType())

template <template <typename> class F, bool B, typename T>
constexpr std::enable_if_t<B == F<T>::value, std::tuple<T>>
   filterType ();

template <template <typename> class F, bool B, typename T>
constexpr std::enable_if_t<B != F<T>::value, std::tuple<>>
   filterType ();

The TheAnswerkelas menjadi

template <typename, template <typename> class>
struct TheAnswer;

template <typename ... Ts, template <typename> class F>
struct TheAnswer<std::variant<Ts...>, F>
 {
   using V1 = decltype(tuple2variant(std::declval<decltype(
                 std::tuple_cat( filterType<F, true, Ts>()... ))>()));
   using V2 = decltype(tuple2variant(std::declval<decltype(
                 std::tuple_cat( filterType<F, false, Ts>()... ))>()));
 };

dan TAdeklarasi juga berlakustd::is_arithmetic

using TA = TheAnswer<std::variant<bool, char, std::string, int, float,
                                  double, std::vector<int>>,
                     std::is_arithmetic>;

Berikut ini adalah contoh kompilasi lengkap dengan std::is_arithmeticsebagai parameter dan V2case kosong

#include <tuple>
#include <string>
#include <vector>
#include <variant>
#include <type_traits>

std::variant<std::monostate> tuple2variant (std::tuple<> const &);

template <typename ... Ts>
std::variant<Ts...> tuple2variant (std::tuple<Ts...> const &);

template <template <typename> class F, bool B, typename T>
constexpr std::enable_if_t<B == F<T>::value, std::tuple<T>>
   filterType ();

template <template <typename> class F, bool B, typename T>
constexpr std::enable_if_t<B != F<T>::value, std::tuple<>>
   filterType ();

template <typename, template <typename> class>
struct TheAnswer;

template <typename ... Ts, template <typename> class F>
struct TheAnswer<std::variant<Ts...>, F>
 {
   using V1 = decltype(tuple2variant(std::declval<decltype(
                 std::tuple_cat( filterType<F, true, Ts>()... ))>()));
   using V2 = decltype(tuple2variant(std::declval<decltype(
                 std::tuple_cat( filterType<F, false, Ts>()... ))>()));
 };

int main ()
 {
   using TA = TheAnswer<std::variant<bool, char, std::string, int, float,
                                     double, std::vector<int>>,
                        std::is_arithmetic>;
   using TB = TheAnswer<std::variant<bool, char, int, float, double>,
                        std::is_arithmetic>;

   using VA1 = std::variant<bool, char, int, float, double>;
   using VA2 = std::variant<std::string, std::vector<int>>;
   using VB1 = VA1;
   using VB2 = std::variant<std::monostate>;

   static_assert( std::is_same_v<VA1, TA::V1> );
   static_assert( std::is_same_v<VA2, TA::V2> );
   static_assert( std::is_same_v<VB1, TB::V1> );
   static_assert( std::is_same_v<VB2, TB::V2> );
 }
maks66
sumber
Solusi Anda tidak berhasil void.
xskxzr
@xskxzr - Maaf tapi saya tidak mengerti keberatan Anda. void, sejauh yang saya tahu, dilarang sebagai ketik a std::variant.
Maks66
1
Buruk saya, saya tidak menyadari std::variant<void>itu buruk, tetapi tampaknya baik std::variant<>-baik saja jika definisi tidak dipakai .
xskxzr