首页 > 解决方案 > 在编译时使整数序列唯一

问题描述

假设我有:

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

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

标签: c++templatesc++17template-meta-programming

解决方案


使用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使用折叠表达式N推入Ms还是丢弃它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 上试用:演示


推荐阅读