如何实现 constexpr N 路set_union(重复数据删除合并)

How can I implement a constexpr N-way set_union (deduplicated merge)

本文关键字:union 数据 合并 删除 何实现 实现 constexpr set      更新时间:2024-04-28

我有一堆结构,保存不同数量的size_t的预排序std::array。举个玩具示例,假设我们有以下三个结构:

struct F_A { static constexpr std::array<size_t, 4> bounds = { 0, 100, 200, 300 }; };
struct F_B { static constexpr std::array<size_t, 5> bounds = { 0, 125, 250, 300, 500 }; };
struct F_C { static constexpr std::array<size_t, 4> bounds = { 100, 250, 300, 301 }; };

目标是在编译时执行相当于N方式std::set_union;例如,给定上述结构,我希望能够编写

constexpr auto bounds = merge_bounds<F_A,F_B,F_C>();

并以bounds作为包含值的constexpr std::array<size_t, 8>0, 100, 125, 200, 250, 300, 301, 500.

一对结构合并bounds数组非常容易;但是,对于如何最好地概括它以使用可变参数模板和参数包,我有点不知所措。为了使具有对的版本正常工作,我首先"模拟"合并以确定合并后的数组在实际进行合并之前需要多长时间,但是这种方法在与参数包组合时变得非常棘手。(我怀疑即使是我的成对代码也远不如我更好地处理一些相关语言功能时那么优雅......

这是一个 MWE 演示了我的成对功能代码:

#include <cstdlib>
#include <iostream>
#include <array>
struct F_A { static constexpr std::array<size_t, 4> bounds = { 0, 100, 200, 300 }; };
struct F_B { static constexpr std::array<size_t, 5> bounds = { 0, 125, 250, 300, 500 }; };
struct F_C { static constexpr std::array<size_t, 4> bounds = { 100, 250, 300, 301 }; };
template <typename F0, typename F1>
inline static constexpr auto merged_size()
{
constexpr auto bnd0 = F0::bounds;
constexpr auto bnd1 = F1::bounds;
size_t i = 0, i0 = 0, i1 = 0;
while (i0 < bnd0.size() && i1 < bnd1.size())
{
if (bnd0[i0] < bnd1[i1]) { i++; i0++;       }
else if (bnd0[i0] > bnd1[i1]) { i++;       i1++; }
else                          { i++; i0++; i1++; }
}
while (i0 < bnd0.size())          { i++; i0++;       }
while (i1 < bnd1.size())          { i++;       i1++; }
return i;
}
template <typename F0, typename F1, size_t N = merged_size<F0,F1>()>
inline static constexpr auto merge_bounds()
{
std::array<size_t, N> merged = { 0 };
constexpr auto bnd0 = F0::bounds;
constexpr auto bnd1 = F1::bounds;
size_t i = 0, i0 = 0, i1 = 0;
while (i0 < bnd0.size() && i1 < bnd1.size())
{
if (bnd0[i0] < bnd1[i1]) { merged[i++] = bnd0[i0++];             }
else if (bnd0[i0] > bnd1[i1]) { merged[i++] =             bnd1[i1++]; }
else                          { merged[i++] = bnd0[i0++];      i1++;  }
}
while (i0 < bnd0.size())          { merged[i++] = bnd0[i0++];             }
while (i1 < bnd1.size())          { merged[i++] =             bnd1[i1++]; }
return std::move(merged);
}
int main(int argc, char * argv[])
{
std::cout << merged_size<F_A,F_B>() << "," << merged_size<F_B,F_C>() << "," << merged_size<F_A,F_C>() << std::endl;
for (auto i : merge_bounds<F_A,F_B>()) std::cout << i << " ";
std::cout <<"n";
for (auto i : merge_bounds<F_B,F_C>()) std::cout << i << " ";
std::cout <<"n";
for (auto i : merge_bounds<F_A,F_C>()) std::cout << i << " ";
std::cout <<"n";
return 0;
}

如何泛化merge_bounds以允许将任意数量的此类结构指定为模板参数?

拥抱价值观。

template<class T, std::size_t N>
struct partial_array:std::array<T,N>{
std::size_t partial=N;
constexpr std::size_t size()const{return partial;}
constexpr T* end()const{return this->begin()+partial;}
//etc
};
template<class T, std::size_t N, std::size_t M, std::size_t...Ms>
constexpr partial_array<T,N+M> merge(partial_array<T,N>,partial_array<T,M>);
template<class T, std::size_t N, std::size_t M>
constexpr partial_array<T,N+(M+Ms...)> merge(partial_array<T,N> a,partial_array<T,M> b, partial_array<T,Ms>... cs){
return merge( a, merge(b,cs...) );
}

现在你只需获取数组,将它们转换为部分数组,合并它们。 结果是一个具有 constexpr 大小的 constexpr 部分数组。

将该 constexpr 大小转换为数组边界,然后复制数据。

template <class...Ts>
constexpr auto merge_bounds() {
constexpr auto merged = merge(partial_array{Ts::bounds}...);// do some magic to make this compile; maybe deduction guilds and a ctor?
std::array<T,merged.size()> retval = merged; // add an operator std::array<T,X> to partial array
return retval;
}

代码可能充满了错别字,但我希望你明白这个想法。