Dividi un determinato tipo std :: variant in base a un determinato criterio


20

Come da un determinato tipo di variante

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

dichiarare due tipi di variante

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

da dove V1include tutti i tipi aritmetici Ve V2comprende tutti i tipi non aritmetici V?

V può essere un parametro di una classe modello, ad esempio:

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

in generale i criteri possono essere una constexprvariabile come questa:

template <class T>
constexpr bool filter;

Risposte:


6

Se per qualsiasi motivo non vuoi usare la risposta breve e ragionevole di Barry, qui non c'è nessuno dei due (grazie @ xskxzr per aver rimosso l'imbarazzante specializzazione "bootstrap", e @ max66 per avermi messo in guardia contro il caso d'angolo della variante vuota) :

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>;

Guardalo dal vivo su Wandbox


Forse puoi decomprimere direttamente Types...dentro std::variant, in questo modo ?
xskxzr

Scusa, ma ... per quanto ne so, un vuoto std::variantè mal formato.
max66

@ max66 Apparentemente solo un'istanza std::variant<> è mal formata, quindi sono in chiaro. Lo modificherò in modo tale V1e V2tornerò a std::variant<std::monostate>pensarci.
Quentin,

Ah ... mal formato solo se istanziato ... OK; mi sembra ragionevole.
max66

14

Con Boost.Mp11 , questo è un breve liner (come sempre):

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

Puoi anche usare:

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

per rendere i due più simmetrici.


In alternativa,

using P = mp_partition<V, std::is_arithmetic>;
using V1 = mp_first<P>;
using V2 = mp_second<P>;

Su quali idee si mp_filterbasa?
Alexey Starinsky il

@AlexeyStarinsky Non capisco la domanda: cosa intendi, quali idee?
Barry

3
@AlexeyStarinsky Leggi la documentazione, si collega anche ad alcuni post che Peter ha scritto, è piuttosto informativo.
Barry

4
@MaximEgorushkin È la migliore libreria di metaprogrammazione imo. Ho molte risposte qui che iniziano con "Con Boost.Mp11, questa è una breve riga"
Barry

1
@Barry Sto leggendo i documenti in questo momento e sembra molto meglio di boost.MPL.
Maxim Egorushkin,

2

EDIT Dato che una variante vuota ( std::variant<>) è mal formata (secondo cppreference ) e che dovrebbe essere usata std::variant<std::monostate>invece, ho modificato la risposta (aggiunta una tuple2variant()specializzazione per la tupla vuota) per supportare il caso quando l'elenco dei tipi per V1o V2è vuoto.


È un po ' decltype()delirante ma ... se dichiari una coppia di filtri helper come segue

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 ();

e una funzione da tupla a variante (con una specializzazione per tuple vuote, per evitare un vuoto std::variant)

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

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

la tua classe semplicemente (?) diventa

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>()... ))>()));
 };

Se si desidera qualcosa di più generico (se si desidera passare std::arithmeticcome parametro modello), è possibile modificare la filterArithm()funzione passando un parametro filtro modello modello F(rinominato 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 ();

La TheAnswerclasse diventa

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>()... ))>()));
 };

e anche la TAdichiarazionestd::is_arithmetic

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

Di seguito è riportato un esempio di compilazione completo con std::is_arithmeticcome parametro e un V2caso vuoto

#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> );
 }

La tua soluzione non funziona per void.
xskxzr

@xskxzr - Scusa ma non capisco la tua obiezione. void, per quanto ne so, è vietato digitare a std::variant.
max66

1
Mio male, non mi rendevo conto che std::variant<void>è mal formato, ma sembra che std::variant<>sia OK se la sua definizione non è istanziata .
xskxzr
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.