按给定条件拆分给定 std::variant 类型
Split a given std::variant type by a given criteria
如何按给定的变体类型
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
可以作为模板的参数class,例如:
template <class V>
struct TheAnswer
{
using V1 = ?;
using V2 = ?;
};
一般来说,条件可以是一个 constexpr
变量,如下所示:
template <class T>
constexpr bool filter;
对于 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>;
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 &);
你的 class 只是 (?) 变成
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
变成class
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
,空case是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> );
}
如果出于某种原因你不想使用 Barry 的简短而合理的答案,这里有一个两者都不是的答案(感谢@xskxzr for removing the awkward "bootstrap" specialization, and to @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>;
如何按给定的变体类型
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
可以作为模板的参数class,例如:
template <class V>
struct TheAnswer
{
using V1 = ?;
using V2 = ?;
};
一般来说,条件可以是一个 constexpr
变量,如下所示:
template <class T>
constexpr bool filter;
对于 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>;
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 &);
你的 class 只是 (?) 变成
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
变成class
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
,空case是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> );
}
如果出于某种原因你不想使用 Barry 的简短而合理的答案,这里有一个两者都不是的答案(感谢@xskxzr for removing the awkward "bootstrap" specialization, and to @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>;