แบ่ง std :: variant type ที่กำหนดตามเกณฑ์ที่กำหนด


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

หากด้วยเหตุผลใดก็ตามที่คุณไม่ต้องการใช้คำตอบสั้น ๆ และสมเหตุสมผลของ Barry นี่คือคำตอบที่ไม่ใช่ (ขอบคุณ @ 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>ว่า
Quentin

อ่า ... เกิดมาไม่ดีก็ต่อเมื่ออินสแตนซ์ ... ตกลง; ดูเหมือนว่าสมเหตุสมผลกับฉัน
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มีพื้นฐานมาจากอะไร?
Alexey Starinsky

@AlexeyStarinsky ฉันไม่เข้าใจคำถาม - คุณหมายถึงอะไรความคิดอะไร
Barry

3
@AlexeyStarinsky อ่านเอกสารมันยังเชื่อมโยงไปยังโพสต์บางอย่างที่ Peter เขียนมันมีข้อมูลมาก
Barry

4
@MaximEgorushkin มันเป็นห้องสมุด metaprogramming ที่ดีที่สุด ฉันมีคำตอบมากมายที่นี่ซึ่งเริ่มต้นด้วย "ด้วย Boost.Mp11 นี่เป็นหนึ่งในสายการบินสั้น"
Barry

1
@Barry ฉันกำลังอ่านเอกสารตอนนี้และมันดูดีกว่า boost.MPL
Maxim Egorushkin

2

แก้ไขเนื่องจากตัวแปรว่างเปล่า ( std::variant<>) เกิดขึ้นไม่ดี (ตามcppreference ) และควรใช้std::variant<std::monostate>แทนฉันได้แก้ไขคำตอบ (เพิ่มความtuple2variant()เชี่ยวชาญสำหรับ tuple ว่าง) เพื่อรองรับกรณีเมื่อรายการประเภทสำหรับ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 ();

และ tuple ฟังก์ชั่นที่แตกต่างกัน (ที่มีความเชี่ยวชาญสำหรับอันดับที่ว่างเปล่าเพื่อหลีกเลี่ยงที่ว่างเปล่า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พารามิเตอร์ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.