在编译时使整数序列唯一

假设我有:

template<int... N>
class seq
{
};

template<int... N>
struct uniq{
    using type = seq<N...>;
};

我需要以某种方式使序列唯一,以便

std::is_same_v<uniq<1,2,2,2,3,3,3>::type, seq<1, 2, 3>>; 

最终是真的。换句话说,使序列唯一,然后创建一个 seq。
有没有办法在编译时实现这一点?

回答

使用 std

使用<type_traits>标准库,您可以像这样实现自己的:

#include <type_traits>

namespace detail
{
template<class, auto... Ns>
struct uniq_impl;
template<template<auto...> class T, auto... Ms, auto N, auto... Ns>
struct uniq_impl<T<Ms...>, N, Ns...> : std::conditional_t<
    (... || (N == Ms)),
    uniq_impl<T<Ms...>, Ns...>,
    uniq_impl<T<Ms..., N>, Ns...>>
{
};
template<template<auto...> class T, auto... Ms>
struct uniq_impl<T<Ms...>>
{
    using type = T<Ms...>;
};
} // namespace detail

template<int... Ns>
class seq
{
};

template<int... Ns>
using uniq = detail::uniq_impl<seq<>, Ns...>;

static_assert(std::is_same_v<typename uniq<1,2,2,2,3,3,3>::type, seq<1, 2, 3>>);

uniq_impl工作原理是从一个空seq<>的参数包开始auto... Ns,然后使用模板特化一次取一个参数包的前面

template<template<auto...> class T, auto... Ms, auto N, auto... Ns>
struct uniq_impl<T<Ms...>, N, Ns...> : std::conditional_t<
    (... || (N == Ms)),
    uniq_impl<T<Ms...>, Ns...>,
    uniq_impl<T<Ms..., N>, Ns...>>
{
};

它检查是否N是在该组的auto... Ms使用倍的表达,并决定是否推NMs或使用丢弃它std::conditional_t。一旦auto... Ns为空,则使用特化

template<template<auto...> class T, auto... Ms>
struct uniq_impl<T<Ms...>>
{
    using type = T<Ms...>;
};

标记生成的唯一值容器。在 Godbolt.org 上试用:演示。

使用 boost::mp11

正如其他人指出的那样,您可以将算法委托给boost::mp11::mp_unique,但因为它适用于类型而不是值,您需要将值包装和解开std::integral_constant来使用这种方法:

#include <boost/mp11/algorithm.hpp>

namespace detail
{
template<template<auto...> class T, auto... Ns>
class uniq_impl
{
    static boost::mp11::mp_list<std::integral_constant<decltype(Ns), Ns>...> types();

    template <class L>
    static boost::mp11::mp_unique<L> transform(L);

    template<class... Ts, auto... Ms>
    static T<Ms...> values(boost::mp11::mp_list<std::integral_constant<Ts, Ms>...>);

public:
    using type = decltype(values(transform(types()))); 
};
} // namespace detail

template<int... Ns>
class seq
{
};

template<int... Ns>
using uniq = detail::uniq_impl<seq, Ns...>;

static_assert(std::is_same_v<typename uniq<1,2,2,2,3,3,3>::type, seq<1, 2, 3>>);

在 Godbolt.org 上试用:演示。

  • That is a very succinct and expressive example of some very pretty meta-programming. And it works for unsorted integer sequences too to boot.

回答

您可以为此使用boost::mp11::mp_unique。

示例:

#include <boost/mp11.hpp>

namespace
{
template <int... N>
using seq = boost::mp11::mp_list_c<int, N...>;

template <int... N>
struct uniq
{
    using type = boost::mp11::mp_unique<seq<N...>>;
};
}

int main()
{
    static_assert(std::is_same_v<uniq<1,2,2,2,3,3,3>::type, seq<1,2,3>>);
    static_assert(std::is_same_v<uniq<4,1,9,9,2,2,3,1,5>::type, seq<4,1,9,2,3,5>>);
    return 0;
}

如果别名不适合seq,您可以执行以下操作:

template <int... N>
struct seq
{};

template <int... N>
struct uniq
{
private:
    template <int... Is>
    static constexpr auto uniquer(boost::mp11::mp_list_c<int, Is...>) -> seq<Is...>;

public:
    using type = decltype(uniquer(boost::mp11::mp_unique<boost::mp11::mp_list_c<int, N...>>{}));
};


以上是在编译时使整数序列唯一的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>