Tách một loại biến std :: đã cho theo một tiêu chí nhất định


20

Làm thế nào để bằng một loại biến thể nhất định

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

khai báo hai loại biến thể

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

trong đó V1bao gồm tất cả các loại số học từ VV2bao gồm tất cả các loại không số học từ Vđâu?

V có thể là một tham số của lớp mẫu, ví dụ:

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

nói chung các tiêu chí có thể là một constexprbiến như thế này:

template <class T>
constexpr bool filter;

Câu trả lời:


6

Nếu vì bất kỳ lý do gì bạn không muốn sử dụng câu trả lời ngắn gọn và hợp lý của Barry, thì đây không phải là câu trả lời (cảm ơn @ xskxzr vì đã loại bỏ chuyên môn "bootstrap" vụng về và @ max66 để cảnh báo tôi về trường hợp góc biến thể trống) :

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

Xem nó trực tiếp trên Wandbox


Có lẽ bạn có thể giải nén Types...bên trong std::varianttrực tiếp, như thế này ?
xskxzr

Xin lỗi, nhưng ... theo tôi biết, một sản phẩm nào đó không std::variantđược định hình đúng.
max66

@ max66 Rõ ràng chỉ instantiating std::variant<> là vô hình thành, vì vậy tôi đang ở rõ ràng. Tôi sẽ điều chỉnh nó để mặc dù vậy V1V2quay trở lại std::variant<std::monostate>mặc dù.
Quentin

À ... chỉ hình thành nếu được khởi tạo ... OK; có vẻ hợp lý với tôi
max66

14

Với Boost.Mp11 , đây là một lớp lót ngắn (như mọi khi):

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

Bạn cũng có thể dùng:

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

để làm cho hai đối xứng hơn.


Ngoài ra,

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

Ý tưởng này mp_filterdựa trên cái gì?
Alexey Starinsky

@AlexeyStarinsky Tôi không hiểu câu hỏi - Ý bạn là gì, ý tưởng gì?
Barry

3
@AlexeyStarinsky Đọc tài liệu, nó cũng liên kết đến một số bài viết mà Peter đã viết, nó khá nhiều thông tin.
Barry

4
@MaximEgorushkin Đây là thư viện siêu lập trình imo tốt nhất. Tôi có rất nhiều câu trả lời ở đây bắt đầu bằng "Với Boost.Mp11, đây là một đoạn ngắn"
Barry

1
@Barry Tôi đang đọc các tài liệu ngay bây giờ và nó có vẻ tốt hơn nhiều so với boost.MPL.
Maxim Egorushkin

2

EDIT Cho rằng một biến thể trống ( std::variant<>) không được hình thành (theo cppreference ) và nên được sử dụng std::variant<std::monostate>thay thế, tôi đã sửa đổi câu trả lời (thêm một tuple2variant()chuyên ngành cho tuple trống) để hỗ trợ trường hợp khi danh sách các loại cho V1hoặc V2trống.


Đó là một chút decltype()mê sảng nhưng ... nếu bạn khai báo một vài bộ lọc trợ giúp như sau

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

và một tuple cho chức năng biến thể (với một chuyên môn cho các bộ dữ liệu trống, để tránh một bộ trống std::variant)

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

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

lớp học của bạn đơn giản (?) trở thành

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

Nếu bạn muốn một cái gì đó chung chung hơn (nếu bạn muốn truyền std::arithmeticdưới dạng tham số mẫu), bạn có thể sửa đổi filterArithm()hàm truyền tham số bộ lọc mẫu-mẫu F(được đổi tên 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 ();

Các TheAnswerlớp học trở thành

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

TAtuyên bố cũng mấtstd::is_arithmetic

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

Sau đây là một ví dụ biên dịch đầy đủ với std::is_arithmetictham số và V2trường hợp trống

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

Giải pháp của bạn không hiệu quả void.
xskxzr

@xskxzr - Xin lỗi nhưng tôi không hiểu sự phản đối của bạn. void, theo như tôi biết, bị cấm là loại trong a std::variant.
max66

1
Thật tệ, tôi đã không nhận ra std::variant<void>là không thành hình, nhưng có vẻ std::variant<>ổn nếu định nghĩa của nó không được khởi tạo .
xskxzr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.