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>>);
注意:constexprstd::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>>);