C++ 在编译时使整数序列唯一

C++ 在编译时使整数序列唯一,c++,templates,c++17,template-meta-programming,C++,Templates,C++17,Template Meta Programming,假设我有: template<int... N> class seq { }; template<int... N> struct uniq{ using type = seq<N...>; }; 模板 类别seq { }; 模板 结构统一{ 使用类型=seq; }; 我需要以某种方式使序列唯一,这样 std::is_same_v<uniq<1,2,2,2,3,3,3>::type, seq<1, 2, 3>>

假设我有:

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

template<int... N>
struct uniq{
    using type = seq<N...>;
};
模板
类别seq
{
};
模板
结构统一{
使用类型=seq;
};
我需要以某种方式使序列唯一,这样

std::is_same_v<uniq<1,2,2,2,3,3,3>::type, seq<1, 2, 3>>; 
std::是相同的吗;
结果是真的。换句话说,使序列唯一,然后创建一个序列。
有没有办法在编译时实现这一点?

您可以使用它

:


要删除相邻的重复项(对于
std::unique
),可以执行以下操作:

template <typename Seq, typename Res = std::index_sequence<>>
struct reverse;

template <typename Res>
struct reverse<std::index_sequence<>, Res>
{
    using type = Res;
};

template <std::size_t I, std::size_t ... Is, std::size_t ... Js>
struct reverse<std::index_sequence<I, Is...>, std::index_sequence<Js...>> : reverse<std::index_sequence<Is...>, std::index_sequence<I, Js...>>
{};

template <typename Seq, typename Res = std::index_sequence<>>
struct uniq;

template <typename Res>
struct uniq<std::index_sequence<>, Res>
{
    using type = typename reverse<Res>::type;    
};

template <std::size_t I, std::size_t ... Is, std::size_t ... Js>
struct uniq<std::index_sequence<I, Is...>, std::index_sequence<I, Js...>> : uniq<std::index_sequence<Is...>, std::index_sequence<I, Js...>> {};

template <std::size_t I, std::size_t ... Is, std::size_t ... Js>
struct uniq<std::index_sequence<I, Is...>, std::index_sequence<Js...>> : uniq<std::index_sequence<Is...>, std::index_sequence<I, Js...>> {};

static_assert(std::is_same_v<reverse<std::index_sequence<3, 2, 1>>::type, std::index_sequence<1, 2, 3>>); 
static_assert(std::is_same_v<uniq<std::index_sequence<1,2,2,2,3,3,3>>::type, std::index_sequence<1, 2, 3>>);

注意:constexpr
std::vector
通常甚至允许删除lambda中的重复代码。

使用
std
使用标准库,您可以实现自己的功能,如下所示:

#包括
名称空间详细信息
{
模板
结构uniq_impl;
模板使用C++20,仅限STL
我在
gcl
库中使用了类似的东西。
(请注意,如果编译器在未计算的上下文中允许lambda,则可以用IIFElambda替换
重复数据消除\u值

现场示例如下:

模板库代码:

模板
constexpr自动元组\u擦除\u重复\u值()
{
constexpr auto arguments=std::tuple{values…};
constexpr auto-element_if_谓词=[arguments]()consteval{
constexpr auto具有\u previous\u position=[&参数](std::index\u序列)consteval{
返回(((std::get(arguments)=argument)| |…);
}(std::make_index_sequence{});
如果constexpr(具有上一个位置)
返回std::tuple{};
else返回std::tuple{argument};
};
返回[&arguments,element_if_谓词](std::index_序列)consteval{
返回std::tuple_cat(element_if_predicate.template操作符()()…);
}(std::make_index_sequence{});
}
静态断言(元组擦除重复值()=std::元组{'a','b',1,2});
在我看来,它不那么晦涩,因此更易于阅读/维护;同时还保留了编译时间。

此外,它还避免了递归。

模板参数总是会被排序吗?我认为这适用于类型而不是整数序列。@MarekR整数序列可以是类型。我会更新我的答案。@PatrickRoberts你是对的,它变得有点复杂-我已经更新了我的答案。很好。不过,也许可以指出,这需要ori要排序的基本数据包;
静态断言(std::is_same_v)
失败(解析为
std::index_序列
)。@dfrib:类似于(运行时)
std::unique
。排序也是C++20:)中的constexpr,这是一些非常漂亮的元编程的一个非常简洁和表达的示例。它也适用于未排序的整数序列。
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...>>{}));
};
template <typename Seq, typename Res = std::index_sequence<>>
struct reverse;

template <typename Res>
struct reverse<std::index_sequence<>, Res>
{
    using type = Res;
};

template <std::size_t I, std::size_t ... Is, std::size_t ... Js>
struct reverse<std::index_sequence<I, Is...>, std::index_sequence<Js...>> : reverse<std::index_sequence<Is...>, std::index_sequence<I, Js...>>
{};

template <typename Seq, typename Res = std::index_sequence<>>
struct uniq;

template <typename Res>
struct uniq<std::index_sequence<>, Res>
{
    using type = typename reverse<Res>::type;    
};

template <std::size_t I, std::size_t ... Is, std::size_t ... Js>
struct uniq<std::index_sequence<I, Is...>, std::index_sequence<I, Js...>> : uniq<std::index_sequence<Is...>, std::index_sequence<I, Js...>> {};

template <std::size_t I, std::size_t ... Is, std::size_t ... Js>
struct uniq<std::index_sequence<I, Is...>, std::index_sequence<Js...>> : uniq<std::index_sequence<Is...>, std::index_sequence<I, Js...>> {};

static_assert(std::is_same_v<reverse<std::index_sequence<3, 2, 1>>::type, std::index_sequence<1, 2, 3>>); 
static_assert(std::is_same_v<uniq<std::index_sequence<1,2,2,2,3,3,3>>::type, std::index_sequence<1, 2, 3>>);
template <std::size_t ... Is, std::size_t ... Js>
consteval auto unique_impl(std::index_sequence<Is...>, std::index_sequence<Js...>)
{
    constexpr std::array<std::size_t, sizeof...(Is)> arr = [](){
        std::array<std::size_t, sizeof...(Is)> arr{{Is...}};
        //std::sort(arr.begin(), arr.end());
        std::unique(arr.begin(), arr.end());
        return arr;
    }();

    return std::index_sequence<arr[Js]...>{};
}

template <std::size_t ... Is>
consteval auto unique_impl(std::index_sequence<Is...> seq)
{
    constexpr std::size_t size = [](){
        std::array<std::size_t, sizeof...(Is)> arr{{Is...}};
        //std::sort(arr.begin(), arr.end());
        auto it = std::unique(arr.begin(), arr.end());
        return std::distance(arr.begin(), it);
    }();
    return unique_impl(seq, std::make_index_sequence<size>());
}

template <std::size_t ... Is>
using unique = decltype(unique_impl(std::index_sequence<Is...>{}));

static_assert(std::is_same_v<unique<1,2,2,2,3,3,3>, std::index_sequence<1, 2, 3>>);