C++ 比较两组类型是否相等
由于这个问题似乎并没有涵盖所有有用的案例,我决定用我的这个小问题来填补这个空白。如果两个多类型集相等,有没有办法回答C++ 比较两组类型是否相等,c++,c++11,templates,variadic-templates,template-meta-programming,C++,C++11,Templates,Variadic Templates,Template Meta Programming,由于这个问题似乎并没有涵盖所有有用的案例,我决定用我的这个小问题来填补这个空白。如果两个多类型集相等,有没有办法回答 #include <tuple> #include <type_traits> template <typename, typename> struct type_multiset_eq : std::false_type { }; template <typename ... Types1, typename ... Types2&
#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");
}
#包括
#包括
模板
结构类型\u多集\u eq:std::false\u类型
{
};
模板
结构类型_多集_eq
:std::true\u类型
{
//仅当多组类型相等时才应为true_type
};
int main(){
静态断言(type_multiset_eq::value,“err”);
静态断言(!type_multiset_eq::value,“err”);
静态断言(type_multiset_eq::value,“err”);
静态断言(!type_multiset_eq::value,“err”);
}
在回答中,我把重点放在了效率上。该方法可分为四个基本步骤:
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>{}),"!");
}
#包括
#包括您可以使用以下选项:
它将从两侧类型中删除,直到第一个为空,或者第二个不匹配:
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>
{
};
模板
结构从\u元组中删除\u类型\u;
模板
结构从\u元组中删除\u类型\u
{
使用type=std::tuple;
};
模板
结构从\u元组中删除\u类型\u
{
使用type=typename从\-tuple::type中删除\-type \;
};
模板
结构从\u元组中删除\u类型\u
{
使用类型=无效;
};
模板
结构从\u元组中删除\u类型\u
{
使用类型=无效;
};
模板
结构差异类型来自元组;
模板
结构差异类型来自元组
{
使用类型=
typename diff_types_from_tuple::type;
};
模板
结构差异类型来自元组
{
使用类型=元组;
};
模板
结构类型\u多集\u等式:
std::是一样的吗
{
};
有趣的问题
从原始问题(即type\u set\u eq
one)中您的答案(嗯……复制它)中获得灵感,添加一个类型计数器(countT
)并删除helper结构和tag结构,我想您可以简单地编写如下内容
#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");
}
只是为了好玩,我提出了另一个基于类型计数的解决方案(就像第一个一样),它具有相同的复杂性(我想是O(n^2),但有点聪明(在第一个差异处结束检查)
是的,自从提到这个问题以来,这个问题一直困扰着我。。。回答得好!毫无疑问,它的优点是符合c++11。缺点可能是致命的复杂性O(N^3)
。排序的方法非常有效,尽管我不认为我会设法将其重写为c++11,因为我不认为在constexpr函数中不使用helper变量就可以执行sort
。@W.F.-谢谢。我不是计算复杂性方面的专家,但是。。。我知道,我的解决方案很简单,但很复杂。但是我喜欢这个问题,所以我不想再多考虑一点。无论如何:我对我的示例做了一些修改:我删除了第二个my\u和check;感谢sizeof…(Ts1)==sizeof…(Ts2)
检查。@W.F.-另一个小变化:我已经解构了type\u multiset\u eq
(现在类型容器是模板模板参数;不再是std::tuple
)。对我来说,当前的复杂性是O(n²)
,在C++17中使用折叠表达式,countT
可以在O(1)
中完成,而不是O(n)
@W.F.:在运行时,应该没有差异,差异将在编译时完成。很好!但有一件事让我感到困扰,那就是理论的复杂性。。。它是O(N^3)
不是吗?自从提到这个问题以来,我简直不敢相信它不能做得更快。。。我的答案是试图证明它可以在O(N logn)
中完成。不幸的是,我在那里使用了c++14,而c++11可能是一个很大的障碍…@W.F:除非我遗漏了什么,否则实例化的数量是O(n²)
(对于max66(对于c++17,count
可以是O(1)
所以是O(n)
实例化的结果)。那么像短路一样吗?不错!
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;
}
#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");
}
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;
}