问题描述
我有一堆结构体,其中包含不同数量的 std::array
的预排序 size_t
。作为一个玩具示例,假设我们有以下三个结构:
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,301 }; };
目标是在编译时执行相当于 N
-way std::set_union
的;例如,鉴于上述结构,我希望能够编写
constexpr auto bounds = merge_bounds<F_A,F_B,F_C>();
并以 bounds
作为包含值 constexpr std::array<size_t,8>
的 0,301,500
结束。
合并来自对结构的bounds
数组非常容易;但是,对于如何最好地将其概括为使用可变参数模板和参数包,我有点不知所措。为了让成对的版本工作,我首先“模拟”合并以确定合并数组在实际进行合并之前的长度,但是当与参数包结合时,这种方法变得非常麻烦。 (我怀疑即使我的配对代码也远不如我对某些相关语言功能有更好的处理能力...)
#include <cstdlib>
#include <iostream>
#include <array>
struct F_A { static constexpr std::array<size_t,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,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 M>
constexpr partial_array<T,N+(M+Ms...)> merge(partial_array<T,N> a,M> b,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;
}
代码可能有很多错别字,但我希望你能明白。