通过给定条件拆分给定的std :: variant类型


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包括来自的所有算术类型,VV2包括来自的所有非算术类型V

V 可以是模板类的参数,例如:

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

通常,条件可以是这样的constexpr变量:

template <class T>
constexpr bool filter;

Answers:


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基于什么想法?
Alexey Starinsky

@AlexeyStarinsky我不明白这个问题-你是什么意思,有什么想法?
巴里

3
@AlexeyStarinsky阅读文档,它还链接到Peter撰写的一些帖子,内容非常丰富。
巴里

4
@MaximEgorushkin这是最好的元编程库imo。我在这里有很多答案,首先是“使用Boost.Mp11,这是一条短线”
巴里

1
@Barry我正在阅读文档,它看起来比boost.MPL好得多。
Maxim Egorushkin

2

编辑鉴于一个空的变体(std::variant<>)的格式不正确(根据cppreference),应该使用它std::variant<std::monostate>代替,我修改了答案(tuple2variant()为空元组添加了专门化),以支持类型为V1V2为空的情况。


这有点decltype()del妄,但是...如果您按如下方式声明一个辅助过滤器函数对,

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

和元组到变体函数(专门针对空元组,以避免empty 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_arithmeticas参数和一个V2空的case

#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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.