如何检查两个参数包是否相同,忽略它们的内部顺序?
到目前为止,我只有框架(使用std::tuple
),但没有功能。
#include <tuple>
#include <type_traits>
template <typename, typename>
struct type_set_eq : std::false_type
{
};
template <typename ... Types1, typename ... Types2>
struct type_set_eq<std::tuple<Types1...>, std::tuple<Types2...>>
: std::true_type
{
// Should only be true_type if the sets of types are equal
};
int main() {
using t1 = std::tuple<int, double>;
using t2 = std::tuple<double, int>;
using t3 = std::tuple<int, double, char>;
static_assert(type_set_eq<t1, t1>::value, "err");
static_assert(type_set_eq<t1, t2>::value, "err");
static_assert(!type_set_eq<t1, t3>::value, "err");
}
每个类型不允许在一个集合中出现多次。
答案 0 :(得分:6)
Boost.Hana解决方案:
constexpr auto t1 = hana::tuple_t<int, double>;
constexpr auto t2 = hana::tuple_t<double, int>;
constexpr auto t3 = hana::tuple_t<int, double, char>;
auto same = [](auto a, auto b)
{
auto to_occurrences_map = [](auto t)
{
return hana::fold(t, hana::make_map(), [](auto m, auto x)
{
if constexpr(!hana::contains(decltype(m){}, x))
{
return hana::insert(m, hana::make_pair(x, 1));
}
else { return ++(m[x]); }
});
};
return to_occurrences_map(a) == to_occurrences_map(b);
};
static_assert(same(t1, t1));
static_assert(same(t1, t2));
static_assert(!same(t1, t3));
答案 1 :(得分:6)
如果元组中的类型是唯一的,那么如果第一个元组中的所有类型都作为辅助结构的基础,则可以使用继承来回答。例如。 (C ++ 11方法):
#include <tuple>
#include <type_traits>
template <class T>
struct tag { };
template <class... Ts>
struct type_set_eq_helper: tag<Ts>... { };
template <class, class, class = void>
struct type_set_eq: std::false_type { };
template <bool...>
struct bool_pack { };
template <bool... Bs>
using my_and = std::is_same<bool_pack<Bs..., true>, bool_pack<true, Bs...>>;
template <class... Ts1, class... Ts2>
struct type_set_eq<std::tuple<Ts1...>, std::tuple<Ts2...>, typename std::enable_if< (sizeof...(Ts1) == sizeof...(Ts2)) && my_and< std::is_base_of<tag<Ts2>, type_set_eq_helper<Ts1...>>::value... >::value >::type >:
std::true_type { };
int main() {
using t1 = std::tuple<int, double>;
using t2 = std::tuple<double, int>;
using t3 = std::tuple<int, double, char>;
static_assert(type_set_eq<t1, t1>::value, "err");
static_assert(type_set_eq<t1, t2>::value, "err");
static_assert(!type_set_eq<t1, t3>::value, "err");
}
答案 2 :(得分:3)
OP是否想要关注事件的数量(如主题建议 - “无序列表”,或者不是,如type_set_eq
建议的那样,并不是很清楚。
所以,我将介绍两种变体。
从set开始 - 不重要的出现次数,则算法如下:
这两点都很重要 - 因为当只检查第1点时 - 我们有空T1列表的反例,它等于任何东西,当然,对于第2点来说,这是相同的反例(点是对称的)。
要检查某些类型列表中是否存在某种类型 - 请使用此简单类模板:
template <typename V, typename ...T> struct is_present;
template <typename V> // <- type is not present in empty list
struct is_present<V> : std::false_type {};
template <typename V, typename F, typename ...T>
struct is_present<V,F,T...> : std::integral_constant<bool,
// type is present in non-empty list
// if it is first element
std::is_same<V,F>::value
// or it is present in the remaining list-but-first
|| is_present<V,T...>::value> {};
由于我们处于C ++之前的17时代 - 那么fold expression尚不可用,所以下面的内容对于表示折叠而言是必要的:
template <bool ...v> struct all_trues;
template <> struct all_trues<> : std::true_type {};
template <bool f, bool ...v> struct all_trues<f,v...> :
std::integral_constant<bool,
f && all_trues<v...>::value>
{};
然后定义比较两组类型的相等性如下:
template <typename ...T1>
struct are_set_of_types
{
template <typename ...T2>
struct equal_to : all_trues<is_present<T1, T2...>::value..., /*1*/
is_present<T2, T1...>::value...> /*2*/
{};
};
当OP开始实施时,可以使用std::tuple
完成此操作:
template <typename T1, typename T2>
struct type_set_eq;
template <typename ...T1, typename ...T2>
struct type_set_eq<std::tuple<T1...>, std::tuple<T2...>>
: are_set_of_types <T1...>::template equal_to<T2...>
{};
当出现次数很重要时,算法如下:
这两点应保证序列相等而不考虑元素的顺序。
因此,与集合比较的区别在于此类模板:
template <typename V, typename ...T>
struct count_occurences;
template <typename V>
// number of occurrences in empty list is 0
struct count_occurences<V> : std::integral_constant<std::size_t, 0u> {};
template <typename V, typename F, typename ...T>
// number of occurrences in non-empty list is
struct count_occurences<V,F,T...> : std::integral_constant<std::size_t,
// 1 if type is same as first type (or 0 otherwise)
(std::is_same<V,F>::value ? 1u : 0u)
// plus number of occurrences in remaining list
+ count_occurences<V,T...>::value> {};
用于检查两个序列相等的模板,无需考虑订单:
template <typename ...T1>
struct are_unordered_types_sequences
{
// when number of occurrences is important
template <typename ...T2>
struct equal_to : all_trues<
/*1*/ sizeof...(T1) == sizeof...(T2),
/*2*/ (count_occurences<T1, T1...>::value == count_occurences<T1, T2...>::value)...>
{};
};
和元组变体:
template <typename T1, typename T2>
struct type_set_eq;
template <typename ...T1, typename ...T2>
struct type_set_eq<std::tuple<T1...>, std::tuple<T2...>>
: are_unordered_types_sequences<T1...>::template equal_to<T2...>
{};
答案 3 :(得分:2)
当然不是最好的解决方案,但我们可以一次只使用一种类型,看看它是否在另一个列表中。如果我们找不到它们,它们就不平等了。如果我们这样做,请重复两个较小的列表:
template <class A, class B>
struct type_set_eq : std::false_type { };
// base case: two empty packs are equal
template <>
struct type_set_eq<std::tuple<>, std::tuple<>> : std::true_type { };
template <class Lhs, class Done, class Rhs>
struct type_set_eq_impl;
// at least one element in each - we use the middle type to keep track
// of all the types we've gone through from the Ys
template <class X, class... Xs, class... Ds, class Y, class... Ys>
struct type_set_eq_impl<std::tuple<X, Xs...>, std::tuple<Ds...>, std::tuple<Y, Ys...>>
: std::conditional_t<
std::is_same<X,Y>::value,
type_set_eq<std::tuple<Xs...>, std::tuple<Ds..., Ys...>>,
type_set_eq_impl<std::tuple<X, Xs...>, std::tuple<Ds..., Y>, std::tuple<Ys...>>>
{ };
// if we run out, we know it's false
template <class X, class... Xs, class... Ds>
struct type_set_eq_impl<std::tuple<X, Xs...>, std::tuple<Ds...>, std::tuple<>>
: std::false_type
{ };
template <class... Xs, class... Ys>
struct type_set_eq<std::tuple<Xs...>, std::tuple<Ys...>>
: std::conditional_t<
(sizeof...(Xs) == sizeof...(Ys)),
type_set_eq_impl<std::tuple<Xs...>, std::tuple<>, std::tuple<Ys...>>,
std::false_type>
{ };
// shortcut to true
template <class... Xs>
struct type_set_eq<std::tuple<Xs...>, std::tuple<Xs...>>
: std::true_type
{ };
答案 4 :(得分:0)
在 C++17 中,我们可以使用折叠表达式相当简单地解决这个问题:
template <typename T, typename... Rest>
constexpr bool is_one_of_v = (std::is_same_v<T, Rest> || ...);
// Given:
// typename... Types1, typename... Types2
constexpr bool is_same_set_v =
// |{Types1...}| == |{Types2...}| and {Types1...} subset of {Types2...}:
sizeof...(Types1) == sizeof...(Types2)
&& (is_one_of_v<Types1, Types2...> && ...);
// Alternative if you want to allow repeated set elements; more mathematical:
constexpr bool is_same_set_v =
// {Types1...} subset of {Types2...} and vice versa.
(is_one_of_v<Types1, Types2...> && ...)
&& (is_one_of_v<Types2, Types1...> && ...);
向后移植到 C++14 很简单:
template <bool...> struct bools {};
template <bool... Vs>
constexpr bool all_of_v = std::is_same<bools<true, Vs...>, bools<Vs..., true>>::value;
template <bool... Vs>
constexpr bool any_of_v = !all_of_v<!Vs...>;
template <typename T, typename... Rest>
constexpr bool is_one_of_v = any_of_v<std::is_same<T, Rest>::value...>;
// Given:
// typename... Types1, typename... Types2
constexpr bool is_same_set_v =
// |{Types1...}| == |{Types2...}| and {Types1...} subset of {Types2...}:
sizeof...(Types1) == sizeof...(Types2)
&& all_of_v<is_one_of_v<Types1, Types2...>...>;
// Alternative if you want to allow repeated set elements; more mathematical:
constexpr bool is_same_set_v =
// {Types1...} subset of {Types2...} and vice versa.
all_of_v<is_one_of_v<Types1, Types2...>...>
&& all_of_v<is_one_of_v<Types2, Types1...>...>;
可以通过更改这些模板变量以通过结构体来降级到 C++11,如下所示:
template <bool... Vs>
struct all_of {
static constexpr bool value =
std::is_same<bools<true, Vs...>, bools<Vs..., true>>::value;
};