Разделить заданный тип std :: option по заданным критериям


20

Как по заданному варианту

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

объявить два варианта типа

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

где V1включает в себя все арифметические типы из Vи V2включает в себя все неарифметические типы из V?

V может быть параметром класса шаблона, например:

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

в общем случае критерии могут быть constexprтакими:

template <class T>
constexpr bool filter;

Ответы:


6

Если по какой-либо причине вы не хотите использовать короткий и разумный ответ Барри, вот один из них, который не является ни одним (спасибо @ xskxzr за удаление неуклюжей специализации "bootstrap" и @ max66 за предупреждение о пустом варианте углового случая) :

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

Смотрите это в прямом эфире на Wandbox


Может быть, вы можете распаковать Types...внутри std::variantпрямо, как это ?
xskxzr

Извините, но ... насколько я знаю, пустое std::variantплохо сформировано.
max66

@ max66 Видимо только инстанцирование std::variant<> плохо сформировано, так что я в чистоте . Я настрою это так V1и V2вернусь к этому std::variant<std::monostate>.
Квентин

Ах ... плохо сформирован, только если создан ... Хорошо; кажется разумным для меня.
max66

14

С Boost.Mp11 это короткий однострочный (как всегда):

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

Вы также можете использовать:

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

сделать два более симметричными.


С другой стороны,

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

На каких идеях это mp_filterосновано?
Алексей Старинский

@AlexeyStarinsky Я не понимаю вопроса - что вы имеете в виду, какие идеи?
Барри

3
@AlexeyStarinsky Прочтите документацию, там также есть ссылки на некоторые посты, написанные Питером, это довольно информативно.
Барри

4
@MaximEgorushkin Это лучшая библиотека метапрограммирования IMO. У меня есть много ответов здесь, которые начинаются с «С Boost.Mp11, это короткий однострочник»
Барри

1
@ Барри Я сейчас читаю документы, и это выглядит намного лучше, чем boost.MPL.
Максим Егорушкин

2

РЕДАКТИРОВАТЬ Учитывая, что пустой вариант ( std::variant<>) плохо сформирован (в соответствии с cppreference ), и его следует использовать std::variant<std::monostate>вместо этого, я изменил ответ (добавил tuple2variant()специализацию для пустого кортежа), чтобы поддержать случай, когда список типов для V1или V2пуст.


Это немного decltype()бред, но ... если вы объявите пару вспомогательных фильтров следующим образом

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

и функция кортежа к варианту (со специализацией для пустых кортежей, чтобы избежать пустых std::variant)

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

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

ваш класс просто (?) стал

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

Если вы хотите что-то более общее (если вы хотите передать std::arithmeticв качестве параметра шаблона), вы можете изменить filterArithm()функцию, передав параметр фильтра template-template F(переименованный 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 ();

TheAnswerКласс стал

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

и TAдекларацию возьмите такжеstd::is_arithmetic

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

Ниже приведен полный пример компиляции с std::is_arithmeticпараметром as и V2пустым регистром.

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

Ваше решение не работает для void.
xskxzr

@xskxzr - Извините, но я не понимаю вашего возражения. voidНасколько я знаю, запрещено вводить в качестве std::variant.
max66

1
Мой плохой, я не осознавал, что std::variant<void>он плохо сформирован, но, кажется, std::variant<>все в порядке, если его определение не было создано .
xskxzr
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.