Учитывая пакет параметров с переменными аргументами, как можно найти количество уникальных значений в пакете. Я ищу что-то вроде

no_of_uniques<0,1,2,1,2,2>::value // should return 3

Моя рудиментарная реализация выглядит примерно так

template <size_t ... all>
struct no_of_uniques;
// this specialisation exceeds -ftemplate-depth as it has no terminating condition
template <size_t one, size_t ... all>
struct no_of_uniques<one,all...> {
    static const size_t value = no_of_uniques<one,all...>::value;
};
template <size_t one, size_t two, size_t three>
struct no_of_uniques<one,two,three> {
    static const size_t value = (one==two && one==three && two==three) ? 1:
                                (one!=two && two==three) ? 2:
                                (one==two && one!=three) ? 2:
                                (one==three && two!=three) ? 2: 3;
};
template <size_t one, size_t two>
struct no_of_uniques<one,two> {
    static const size_t value = one==two ? 1: 2;
};
template <size_t one>
struct no_of_uniques<one> {
    static const size_t value = 1;
};

Здесь я специализируюсь на трех аргументах, но понятно, что код растет экспоненциально с количеством аргументов. Я хотел бы иметь для этого решение meta, использующее исключительно STL и никаких сторонних библиотек, таких как Boost.MPL.

Аналогичный вопрос, хотя и в контексте проверки уникальных типов, а не поиска количества уникальных значений пакета параметров, можно найти здесь:

Проверяйте параметры вариативных шаблонов на уникальность

В процессе поиска количества уникальных значений пакета параметров нам может потребоваться сначала отсортировать пакет, и отличная реализация этого предоставляется в этом другом вопросе.

Быстрая сортировка во время компиляции с использованием вариативных шаблонов C ++ 11

4
romeric 11 Май 2016 в 02:06

3 ответа

Лучший ответ

Вот простой способ сделать это за O (n ^ 2)

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

Итак, используя ваш пример:

no_unique<0, 1, 2, 1, 2, 2>::value; // gives 3
10
kmdreko 11 Май 2016 в 17:50

По большей части это техника, которую я уже написал для другого вопроса, без части «подсчета».

Пакет с ярлыком sizeof:

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

Добавьте тип в пакет типов, но только если он еще не существует:

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;

Теперь сделайте пачку уникальных видов:

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;

В заключение:

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;
};
6
Community 23 Май 2017 в 11:53

Я обобщу на типы - поскольку метапрограммирование лучше работает с типами. Алгоритм подсчета уникальности: пустой список аргументов имеет 0 уникальных типов, а непустой список имеет 1 уникальный тип + количество уникальных типов в конце этого списка после удаления исходного типа.

Фактически, давайте продолжим обобщение - давайте напишем метафункцию, которая принимает список типов и возвращает уникальные типы в нем. Когда у вас есть уникальные типы, их легко сосчитать.

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

Теперь нам просто нужно заполнить concat и filter_out_t. Последний по сути является прославленным concat:

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

Теперь, имея список типов, мы можем определить уникальные. Чтобы вернуться к исходной проблеме, нам просто нужна метафункция размера:

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

А затем заверните все в один псевдоним:

template <size_t... Ns>
using no_of_uniques = length_t<uniq_t<typelist<size_t_<Ns>...>>>;
2
Barry 11 Май 2016 в 16:55