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


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 за видалення незручної спеціалізації "завантажувач" та @ 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, це короткий однолінійний"
Barry

1
@Barry Я читаю документи прямо зараз, і це виглядає набагато краще, ніж boost.MPL.
Максим Єгорушкін

2

EDIT Зважаючи на те, що порожній варіант ( 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()функцію, передаючи параметр фільтра шаблону шаблону 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параметром як 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.