Find number of unique values of a parameter pack

自闭症网瘾萝莉.ら 提交于 2019-12-01 19:18:06

Here's a simple O(n^2) way to do it

template <size_t...>
struct is_unique : std::integral_constant<bool, true> {};

template <size_t T, size_t U, size_t... VV>
struct is_unique<T, U, VV...> : std::integral_constant<bool, T != U && is_unique<T, VV...>::value> {};

template <size_t...>
struct no_unique : std::integral_constant<size_t, 0> {};

template <size_t T, size_t... UU>
struct no_unique<T, UU...> : std::integral_constant<size_t, is_unique<T, UU...>::value + no_unique<UU...>::value> {};

So using your example:

no_unique<0, 1, 2, 1, 2, 2>::value; // gives 3
T.C.

Most of this is machinery I already wrote for a different question, stripped of the "counting" part.

A pack with a sizeof shortcut:

template<class... Ts> struct pack { 
    static constexpr size_t size = sizeof...(Ts);
};

Add a type to a pack of types, but only if it doesn't exist already:

template<class T, class PT> struct do_push;
template<class T, class...Ts>
struct do_push<T, pack<Ts...>>{
   using type = std::conditional_t<std::disjunction_v<std::is_same<Ts, T>...>,
        pack<Ts...>,
        pack<T, Ts...>
        >;
};
template<class T, class PT> using push = typename do_push<T, PT>::type;

Now make a pack of unique types:

template<class P, class PT = pack<> >
struct unique_types_imp { using type = PT; };

template<class PT, class T, class... Ts>
struct unique_types_imp <pack<T, Ts...>, PT>
        : unique_types_imp <pack<Ts...>, push<T, PT>> {};

template<class P>
using unique_types = typename unique_types_imp<P>::type;

Finally:

template<size_t S>
using size_constant = std::integral_constant<size_t, S>;

template<size_t... all>
struct no_of_uniques{
     static constexpr size_t value = unique_types<pack<size_constant<all>...>>::size;
};

I'll generalize to types - since metaprogramming works better in types. The algorithm for counting uniqueness is an empty argument list has 0 unique types, and a non-empty list has 1 unique type + the number of unique types in the tail of that list after having removed the initial type.

In fact, let's generalize further than that - let's write a metafunction that takes a list of types and returns the unique types in it. Once you have the unique types, it's easy to count them.

template <class... > struct typelist { };

template <class >
struct uniq;

template <class TL>
using uniq_t = typename uniq<TL>::type;

template <>
struct uniq<typelist<>> {
    using type = typelist<>;
};

template <class T, class... Ts>
struct uniq<typelist<T, Ts...>>
    : concat<typelist<T>, uniq_t<filter_out_t<T, typelist<Ts...>>>>
{ };

Now we just need to fill in concat and filter_out_t. The latter is basically a glorified concat anyway:

template <class... > struct concat;
template <> struct concat<> { using type = typelist<>; };

template <class... Ts>
struct concat<typelist<Ts...>> {
    using type = typelist<Ts...>;
};

template <class... Ts, class... Us, class... Args>
struct concat<typelist<Ts...>, typelist<Us...>, Args...>
: concat<typelist<Ts..., Us...>, Args...>
{ };

template <class T, class TL>
struct filter_out;

template <class T, class TL>
using filter_out_t = typename filter_out<T, TL>::type;

template <class T, class... Ts>
struct filter_out<T, typelist<Ts...>>
    : concat<
        std::conditional_t<std::is_same<T, Ts>::value, typelist<>, typelist<Ts>>...
        >
{ };

Now, given a list of types, we can figure out the unique ones. To backport to the original problem, we just need a a size metafunction:

template <size_t N>
using size_t_ = std::integral_constant<size_t, N>;

template <class > struct length;
template <class T> using length_t = typename length<T>::type;
template <class... Ts>
struct length<typelist<Ts...>>
: size_t_<sizeof...(Ts)>
{ };

And then wrap everything up in one alias:

template <size_t... Ns>
using no_of_uniques = length_t<uniq_t<typelist<size_t_<Ns>...>>>;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!