由于this问题似乎并未涵盖所有有用的案例,我决定用我的这个小问题填补空白。如果两个类型的多个集合相等,有没有办法回答?
#include <tuple>
#include <type_traits>
template <typename, typename>
struct type_multiset_eq : std::false_type
{
};
template <typename ... Types1, typename ... Types2>
struct type_multiset_eq<std::tuple<Types1...>, std::tuple<Types2...>>
: std::true_type
{
// Should only be true_type if the multisets of types are equal
};
int main() {
static_assert(type_multiset_eq<std::tuple<char, int, double, float, int, float>, std::tuple<float, char, int, double, int, float>>::value, "err");
static_assert(!type_multiset_eq<std::tuple<char, int, double, float, int, float>, std::tuple<char, int, double, int, float>>::value, "err");
static_assert(type_multiset_eq<std::tuple<char, char, char, float, float, float>, std::tuple<char, float, char, float, char, float>>::value, "err");
static_assert(!type_multiset_eq<std::tuple<int, int>, std::tuple<int, int, int>>::value, "err");
}
答案 0 :(得分:2)
在答案中,我重点关注效率。该方法可分为四个基本步骤:
该方法应为O(N log N)
,具体取决于类型数
C ++ 14方法:
#include <utility>
#include <array>
template <class T>
constexpr T ilog2(T n) {
return n == static_cast<T>(1) ? static_cast<T>(0) : ilog2(n >> static_cast<T>(1)) + 1;
}
template <class T>
constexpr T ipow2(T n) {
return static_cast<T>(1) << n;
}
template <std::size_t N>
struct s_exp {
static constexpr std::size_t exp = ipow2(ilog2(N-1)+1);
};
template <std::size_t I, class T>
struct itag { };
template <std::size_t D, std::size_t I, class T>
struct vvtag { };
template <std::size_t S, std::size_t I, class T>
struct vtag: virtual vvtag<I/S, (I%S) / ((s_exp<S>::exp + (2 << (I/S)) - 1)/(2 << (I/S))), T> { };
template <class... Ts>
struct pack {
static constexpr std::size_t size = sizeof...(Ts);
};
template <class P, class = std::make_index_sequence<P::size>>
struct ipack;
template <class... Ts, std::size_t... Is>
struct ipack<pack<Ts...>, std::index_sequence<Is...>>: itag<Is, Ts>... {
static constexpr std::size_t size = sizeof...(Ts);
};
template <std::size_t I, class T>
T ipack_element(itag<I, T>);
template <class IP, class = std::make_index_sequence<IP::size * (ilog2(IP::size - 1) + 1) >>
struct vpack;
template <class IP, std::size_t... Is>
struct vpack<IP, std::index_sequence<Is...>>: vtag<IP::size, Is, decltype(ipack_element<Is % IP::size>(IP{}))>... {
static constexpr std::size_t size = IP::size;
};
template <class A, class CompArr>
constexpr int partition(A &a, int lo, int hi, const CompArr &ca) {
int x = a[lo];
int i = lo, j = hi;
while (true) {
while (ca[a[j]] > ca[x])
j--;
while (ca[a[i]] < ca[x])
i++;
if (i < j) {
auto w = a[i];
a[i] = a[j];
a[j] = w;
i++;
j--;
} else
return j;
}
}
template <class A, class CompArr>
constexpr void quicksort(A &a, int lo, int hi, const CompArr &ca) {
if (lo < hi) {
auto q = partition(a, lo, hi, ca);
quicksort(a, lo, q, ca);
quicksort(a, q+1, hi, ca);
}
}
template <class... Ts, std::size_t... Is>
constexpr std::array<std::size_t, sizeof...(Ts)> rank(itag<0, ipack<pack<Ts...>, std::index_sequence<Is...>>>) {
return {{!std::is_base_of<vvtag<0, 0, decltype(ipack_element<Is>(ipack<pack<Ts...>>{}))>, vpack<ipack<pack<Ts...>>>>::value...}};
}
template <std::size_t N, class... Ts, std::size_t... Is>
constexpr std::array<std::size_t, sizeof...(Ts)> rank(itag<N, ipack<pack<Ts...>, std::index_sequence<Is...>>>) {
constexpr auto prev = rank(itag<N - 1, ipack<pack<Ts...>>>{});
return {{prev[Is]*2 + !std::is_base_of<vvtag<N, prev[Is]*2, decltype(ipack_element<Is>(ipack<pack<Ts...>>{}))>, vpack<ipack<pack<Ts...>>>>::value...}};
}
template <class... Ts, std::size_t... Is>
constexpr std::array<std::size_t, sizeof...(Ts)> sort_types_impl(ipack<pack<Ts...>, std::index_sequence<Is...>>) {
constexpr std::size_t TS = sizeof...(Ts);
auto compare_enabler = rank(itag<ilog2(TS - 1), ipack<pack<Ts...>, std::index_sequence<Is...>>>{});
std::size_t result[TS] { Is... };
quicksort(result, 0, sizeof...(Is) - 1, compare_enabler);
return {{ result[Is]... }};
}
template <class>
struct sort_types;
template <class... Ts>
struct sort_types<pack<Ts...>>: sort_types<ipack<pack<Ts...>>> { };
template <class... Ts, std::size_t... Is>
struct sort_types<ipack<pack<Ts...>, std::index_sequence<Is...>>> {
static constexpr auto idxs = sort_types_impl(ipack<pack<Ts...>>{});
using type = pack<decltype(ipack_element<idxs[Is]>(ipack<pack<Ts...>>{}))...>;
};
struct dummy { };
template <class... Ts>
struct unique_pack: Ts... {
static constexpr std::size_t size = sizeof...(Ts);
template <class Up>
constexpr bool operator==(Up) {
bool result = size == Up::size;
bool ibo[sizeof...(Ts)] = { std::is_base_of<Ts, Up>::value... };
for (std::size_t i = 0; i < sizeof...(Ts); i++)
result &= ibo[i];
return result;
}
};
template <class>
struct multiset;
template <class... Ts>
struct multiset<pack<Ts...>>: multiset<ipack<pack<Ts...>>> {};
template <class... Ts, std::size_t... Is>
struct multiset<ipack<pack<Ts...>, std::index_sequence<Is...>>> {
using sorted_pack = typename sort_types<pack<Ts..., dummy>>::type;
static constexpr std::array<bool, sizeof...(Ts)> const unique_types() {
return {{ !std::is_same< decltype(ipack_element<Is>(ipack<sorted_pack>{})), decltype(ipack_element<Is + 1>(ipack<sorted_pack>{})) >::value... }};
}
static constexpr std::size_t unique_count() {
constexpr std::array<bool, sizeof...(Ts)> const ut = unique_types();
std::size_t result = 0;
for (std::size_t i = 0; i < sizeof...(Ts); i++)
result += ut[i];
return result;
}
template <std::size_t... Is2>
static constexpr std::array<std::size_t, unique_count()> const unique_idxs(std::index_sequence<Is2...>) {
std::size_t result[unique_count()] {};
std::size_t cur = 0;
constexpr std::array<bool, sizeof...(Ts)> const ut = unique_types();
for (std::size_t i = 0; i < sizeof...(Ts); i++) {
if (ut[i])
result[cur++] = i;
}
return {{ result[Is2]... }};
}
template <std::size_t... Is2>
static constexpr std::array<std::size_t, unique_count()> const unique_counts(std::index_sequence<Is2...>) {
std::size_t result[unique_count()] {};
std::size_t cur = 0;
constexpr auto ut = unique_types();
for (std::size_t i = 0; i < sizeof...(Ts); i++) {
if (ut[i])
result[cur++]++;
else
result[cur]++;
}
return {{ result[Is2]... }};
}
template <std::size_t... Is2>
static auto make_type(std::index_sequence<Is2...>) {
constexpr std::array<std::size_t, unique_count()> const idxs = unique_idxs(std::index_sequence<Is2...>{});
constexpr std::array<std::size_t, unique_count()> const counts = unique_counts(std::index_sequence<Is2...>{});
return unique_pack<itag<counts[Is2], decltype(ipack_element<idxs[Is2]>(ipack<sorted_pack>{}))>...>{};
}
template <class T = multiset, std::size_t UC = T::unique_count()>
using type = decltype(make_type(std::make_index_sequence<UC>{}));
};
template <class P1, class P2>
constexpr bool multiset_equality(P1, P2) {
return typename multiset<P1>::template type<>{} == typename multiset<P2>::template type<>{} && typename multiset<P2>::template type<>{} == typename multiset<P1>::template type<>{};
}
int main() {
static_assert(multiset_equality(pack<char, int, double, float, int, float>{}, pack<float, char, int, double, int, float>{}),"!");
static_assert(!multiset_equality(pack<char, int, double, float, int, float>{}, pack<char, int, double, int, float>{}),"!");
static_assert(multiset_equality(pack<char, char, char, float, float, float>{}, pack<char, float, char, float, char, float>{}),"!");
static_assert(!multiset_equality(pack<int, int>{}, pack<int, int, int>{}),"!");
}
答案 1 :(得分:1)
您可以使用以下内容: 它从两种类型中移除,直到第一个为空,或第二个不匹配:
template <typename T, typename Tuple, typename Res = std::tuple<>>
struct remove_type_from_tuple;
template <typename T, typename ... Ts, typename ...Res>
struct remove_type_from_tuple<T, std::tuple<T, Ts...>, std::tuple<Res...>>
{
using type = std::tuple<Res..., Ts...>;
};
template <typename T, typename T2, typename ... Ts, typename ...Res>
struct remove_type_from_tuple<T, std::tuple<T2, Ts...>, std::tuple<Res...>>
{
using type = typename remove_type_from_tuple<T,
std::tuple<Ts...>,
std::tuple<Res..., T2>>::type;
};
template <typename T, typename Res>
struct remove_type_from_tuple<T, std::tuple<>, Res>
{
using type = void;
};
template <typename T, typename Res>
struct remove_type_from_tuple<T, void, Res>
{
using type = void;
};
template <typename Tuple1, typename Tuple2>
struct diff_types_from_tuple;
template <typename T, typename ...Ts, typename Tuple>
struct diff_types_from_tuple<std::tuple<T, Ts...>, Tuple>
{
using type =
typename diff_types_from_tuple<std::tuple<Ts...>,
typename remove_type_from_tuple<T, Tuple>::type
>::type;
};
template <typename Tuple>
struct diff_types_from_tuple<std::tuple<>, Tuple>
{
using type = Tuple;
};
template <typename Tuple1, typename Tuple2>
struct type_multiset_eq :
std::is_same<std::tuple<>,
typename diff_types_from_tuple<Tuple1, Tuple2>::type>
{
};
答案 2 :(得分:1)
有趣的问题......
从原始问题(type_set_eq
)中的答案中获取灵感(好吧......复制它),添加一个类型计数器(countT
)并删除辅助结构和标记结构,我想你可以简单地写下如下内容
#include <tuple>
#include <type_traits>
template <typename ...>
struct countT;
template <typename T>
struct countT<T>
{ static constexpr std::size_t value { 0U }; };
template <typename T, typename T0, typename ... Ts>
struct countT<T, T0, Ts...>
{ static constexpr std::size_t value { countT<T, Ts...>::value }; };
template <typename T, typename ... Ts>
struct countT<T, T, Ts...>
{ static constexpr std::size_t value { 1U + countT<T, Ts...>::value }; };
template <bool ...>
struct bool_pack
{ };
template <bool ... Bs>
using my_and = std::is_same<bool_pack<Bs..., true>, bool_pack<true, Bs...>>;
template <typename, typename, typename = void>
struct type_multiset_eq : std::false_type
{ };
template <template <typename ...> class C1, typename ... Ts1,
template <typename ...> class C2, typename ... Ts2>
struct type_multiset_eq<C1<Ts1...>, C2<Ts2...>,
typename std::enable_if<
(sizeof...(Ts1) == sizeof...(Ts2))
&& (my_and<( countT<Ts1, Ts1...>::value
== countT<Ts1, Ts2...>::value)...>::value)
>::type>
: std::true_type
{ };
int main()
{
static_assert( type_multiset_eq<
std::tuple<char, int, double, float, int, float>,
std::tuple<float, char, int, double, int, float>>::value, "err");
static_assert( ! type_multiset_eq<
std::tuple<char, int, double, float, int, float>,
std::tuple<char, int, double, int, float>>::value, "err");
static_assert( type_multiset_eq<
std::tuple<char, char, char, float, float, float>,
std::tuple<char, float, char, float, char, float>>::value, "err");
static_assert( ! type_multiset_eq<
std::tuple<int, int>,
std::tuple<int, int, int>>::value, "err");
}
如果您可以使用C ++ 14,则可以使用以下countT
函数替换constexpr
类型特征
template <typename T, typename ... Ts>
constexpr std::size_t cntT ()
{
using unused = std::size_t[];
std::size_t ret { 0U };
(void)unused { 0U, ret += (std::is_same<T, Ts>::value ? 1U : 0U)... };
return ret;
}
答案 3 :(得分:1)
为了好玩,我提出了另一种基于类型计数的解决方案(比如第一种)具有相同的复杂度(O(n ^ 2),我猜)但更聪明一点(结束第一个区别的检查)< / p>
#include <tuple>
#include <type_traits>
template <typename ...>
struct countT;
template <typename T>
struct countT<T>
{ static constexpr std::size_t value { 0U }; };
template <typename T, typename T0, typename ... Ts>
struct countT<T, T0, Ts...>
{ static constexpr std::size_t value { countT<T, Ts...>::value }; };
template <typename T, typename ... Ts>
struct countT<T, T, Ts...>
{ static constexpr std::size_t value { 1U + countT<T, Ts...>::value }; };
template <typename, typename, typename>
struct eqCountT;
template <template <typename ...> class C, typename T, typename ... Ts1,
typename ... Ts2, typename ... Ts3>
struct eqCountT<C<T, Ts1...>, C<Ts2...>, C<Ts3...>>
: std::integral_constant<bool,
(countT<T, Ts2...>::value == countT<T, Ts3...>::value)>
{ };
template <template <typename ...> class C, typename T2, typename T3>
struct eqCountT<C<>, T2, T3> : std::true_type
{ };
template <typename T1, typename T2, typename T3,
bool = eqCountT<T1, T2, T3>::value>
struct mseqH;
template <template <typename ...> class C, typename T2, typename T3>
struct mseqH<C<>, T2, T3, true> : std::true_type
{ };
template <typename T1, typename T2, typename T3>
struct mseqH<T1, T2, T3, false> : std::false_type
{ };
template <template <typename ...> class C, typename T, typename ... Ts1,
typename T2, typename T3>
struct mseqH<C<T, Ts1...>, T2, T3, true> : mseqH<C<Ts1...>, T2, T3>
{ };
template <typename, typename>
struct type_multiset_eq;
template <template <typename ...> class C1, typename ... Ts1,
template <typename ...> class C2, typename ... Ts2>
struct type_multiset_eq<C1<Ts1...>, C2<Ts2...>>
: std::integral_constant<bool,
(sizeof...(Ts1) == sizeof...(Ts2))
&& mseqH<C1<Ts1...>, C1<Ts1...>, C1<Ts2...>>::value>
{ };
int main()
{
static_assert( type_multiset_eq<
std::tuple<char, int, double, float, int, float>,
std::tuple<float, char, int, double, int, float>>::value, "err");
static_assert( ! type_multiset_eq<
std::tuple<char, int, double, float, int, float>,
std::tuple<char, int, double, int, float>>::value, "err");
static_assert( type_multiset_eq<
std::tuple<char, char, char, float, float, float>,
std::tuple<char, float, char, float, char, float>>::value, "err");
static_assert( ! type_multiset_eq<
std::tuple<int, int>,
std::tuple<int, int, int>>::value, "err");
}
如果您可以使用C ++ 14,则可以使用以下countT
函数替换constexpr
类型特征
template <typename T, typename ... Ts>
constexpr std::size_t cntT ()
{
using unused = std::size_t[];
std::size_t ret { 0U };
(void)unused { 0U, ret += (std::is_same<T, Ts>::value ? 1U : 0U)... };
return ret;
}