比较两个类型的多个集合是否相等

时间:2017-04-25 10:11:34

标签: c++ c++11 templates variadic-templates template-meta-programming

由于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");
}

4 个答案:

答案 0 :(得分:2)

在答案中,我重点关注效率。该方法可分为四个基本步骤:

  1. 每个包中的排名类型
  2. 按给定等级基础对包中的类型进行排序
  3. 创建两组独特元素(也包括类型),其中包含有关先前版本包的类型及其频率的信息(继承自这些类型)
  4. 调查其他多种类型是否来自相同类型(带频率)
  5. 该方法应为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>{}),"!");
    }
    

    [live demo]

答案 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>
{
};

Demo

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