目前尚不清楚 OP 是否要关心出现次数(如主题建议 - “无序列表”,或不 - 如 type_set_eq 建议。
所以,我将介绍这两种变体。
从集合开始-出现次数不重要,那么算法如下:
- 对于 T1 中的每种类型,检查它是否存在于 T2 中
- 对于 T2 中的每种类型,检查它是否存在于 T1 中
这两点都很重要 - 因为只检查点 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*/
{};
};
可以用std::tuple作为OP开始实现,这样:
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...>
{};
当出现次数很重要时,算法如下:
- 检查两个序列的大小是否相等
- 检查左序列中每个值的出现次数是否等于第二序列中该值的出现次数
这两点应该保证序列是相等的,而不考虑元素的顺序。
所以,与 set-comparison 的区别在于这个类模板:
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...>
{};
Wandbox example I used to play with these templates