指定されたstd :: variant型を指定された基準で分割します


20

特定のバリアントタイプによる方法

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

2つのバリアント型を宣言する

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

どこV1からすべての算術型を含んVV2から、すべての非算術型が含まれて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<>ので、私は明確にしています。私はそうすることを、それを微調整しますV1V2にフォールバックstd::variant<std::monostate>けれども。
クエンティン

ああ...インスタンス化された場合のみ不正な形式です... OK; 私には理にかなっているようです。
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>;

2つをより対称にするため。


または、

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

これはどのようなアイデアにmp_filter基づいていますか?
アレクセイスタリンスキー

@AlexeyStarinsky私は質問を理解していません-どういう意味ですか、どんなアイデアですか?
バリー

3
@AlexeyStarinskyドキュメントを読んでください。また、Peterが書いたいくつかの投稿にリンクしています。
バリー

4
@MaximEgorushkinこれは、最高のメタプログラミングライブラリimoです。「Boost.Mp11を使用すると、これは短いワンライナーです」から始まる多くの答えがあります
バリー

1
@Barry私は今ドキュメントを読んでおり、boost.MPLよりもはるかに良く見えます。
Maxim Egorushkin

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.