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;
     }