查找参数包的唯一值的数量
Find number of unique values of a parameter pack
给定一个带有可变参数的参数包,如何找到包中唯一值的数量。我正在寻找类似
的东西
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
这样的第三方库。
可以在此处找到一个类似的问题,尽管是在检查唯一类型的上下文中,而不是查找参数包的唯一值的数量:
Check variadic templates parameters for uniqueness
在查找参数包的唯一值数量的过程中,我们可能需要先对包进行排序,另一个问题中提供了一个很好的实现
Quick sort at compilation time using C++11 variadic templates
这是一个简单的 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
使用 Boost.Mp11,这是一个简短的一行(一如既往):
template <size_t... Ns>
using no_of_uniques = mp_size<mp_unique<mp_list<mp_size_t<Ns>...>>>;
遵循与下文所述相同的逻辑。我们将值提升为类型,将它们放入类型列表中,从中获取唯一类型,然后获取长度。
我将归纳为类型——因为元编程在类型方面效果更好。计算唯一性的算法是空参数列表有 0 个唯一类型,非空列表有 1 个唯一类型 + 删除初始类型后该列表尾部的唯一类型数。
事实上,让我们进一步概括 - 让我们编写一个元函数,它接受类型列表和 returns 其中的唯一类型。一旦你有了独特的类型,就很容易计算它们。
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>...>>>;
其中大部分是我已经为 编写的机器,去掉了 "counting" 部分。
带有 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;
};
给定一个带有可变参数的参数包,如何找到包中唯一值的数量。我正在寻找类似
的东西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
这样的第三方库。
可以在此处找到一个类似的问题,尽管是在检查唯一类型的上下文中,而不是查找参数包的唯一值的数量:
Check variadic templates parameters for uniqueness
在查找参数包的唯一值数量的过程中,我们可能需要先对包进行排序,另一个问题中提供了一个很好的实现
Quick sort at compilation time using C++11 variadic templates
这是一个简单的 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
使用 Boost.Mp11,这是一个简短的一行(一如既往):
template <size_t... Ns>
using no_of_uniques = mp_size<mp_unique<mp_list<mp_size_t<Ns>...>>>;
遵循与下文所述相同的逻辑。我们将值提升为类型,将它们放入类型列表中,从中获取唯一类型,然后获取长度。
我将归纳为类型——因为元编程在类型方面效果更好。计算唯一性的算法是空参数列表有 0 个唯一类型,非空列表有 1 个唯一类型 + 删除初始类型后该列表尾部的唯一类型数。
事实上,让我们进一步概括 - 让我们编写一个元函数,它接受类型列表和 returns 其中的唯一类型。一旦你有了独特的类型,就很容易计算它们。
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>...>>>;
其中大部分是我已经为
带有 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;
};