我想知道如何在 C++ 中执行以下操作:
考虑这些类:
C1 < C2 < C3 < ... < Cn,
Ci < Cj
表示 sizeof(Ci) < sizeof(Cj)
我想要一个使用可变参数模板作为 Ci
序列的结构的,
OrderBySize<AnySequenceOfCis>
,例如:OrderBySize<C1,C2,C3,...,Cn>
或者
OrderBySize<C2,C1,C3,C4,...,Cn>
...所有可能的组合
并给出以下结构作为结果:
class result{
Cn elem1;
Cn-1 elem2;
.
.
.
C1 elemn;
}
我读了this article , 它显示了我们如何定义 Tuple<typename ...T>
但是,这是不同的,实现起来要困难得多,但非常有用。
编辑:
order_by_size<T1, ..., Tn>
将包含 T1, ..., Tn
的有序组合的元组
但是我不想让用户知道我正在对字段进行排序,用户会像使用元组一样使用它。因此,为了访问字段,用户将使用:
template<typename... Tn>
get<size_t>(const MyStructure<Tn ...>& m)
得到size_t
第 ' 个元素在新的 tuple
中有另一个索引.
最佳答案
基本上,这个问题简化为仅根据给定的比较器对类型列表进行排序。一旦你有了它,其他一切都会随之而来。所以这个答案只是排序部分。我们将从一个类型列表开始:
template <typename...>
struct typelist {
using type = typelist;
};
我将假设一组非常短的元函数(head
、tail
、concat
、size
)。为了简洁起见,我将省略它们。
那么让我们开始编写归并排序:
template <typename TL, typename Cmp = LessSize>
struct sort
{
using left_right = typename split<TL, size<TL>::value/2>::type;
using left = typename sort<head_t<left_right>, Cmp>::type;
using right = typename sort<head_t<tail_t<left_right>>, Cmp>::type;
using type = typename merge<left, right, Cmp>::type;
};
// base case for exactly 1 element
template <typename T, typename Cmp>
struct sort<typelist<T>, Cmp> {
using type = typelist<T>;
};
// potentially add a base case for exactly 2 elements here?
这里的一般结构应该看起来很熟悉。我们拆分了类型列表,TL
, 分成两个相等的部分,对两者进行排序,然后合并。当然,这是元编程,所以一切都不必要地复杂。
让我们从split
开始. split
接受一个类型列表和一个大小,并返回一个包含两个类型列表的类型列表:第一个具有给定的大小,第二个是剩余的大小:
template <typename A, typename B, size_t N>
struct split_impl
: std::conditional<
size<A>::value < N,
split_impl<concat_t<A, typelist<head_t<B>>>, tail_t<B>, N>,
typelist<A, B>
>::type
{ };
template <typename TL, size_t N>
struct split
: split_impl<typelist<>, TL, N>
{ };
所以这给了我们 left
和 right
(至少一次我们应用 head_t<>
和 head_t<tail_t<>>
)。剩下的就是merge
步。我正在使用元函数类是什么的 Boost MPL 想法,所以 LessSize
是:
struct LessSize {
template <typename A, typename B>
using apply = std::integral_constant<bool, sizeof(A) < sizeof(B)>;
};
merge
只需要遍历两个类型列表并根据两个类型列表之间的比较器选择最小的元素。首先,我们将从所有基本案例开始:
template <typename L, typename R, typename Cmp>
struct merge;
// R empty
template <typename... T, typename Cmp>
struct merge<typelist<T...>, typelist<>, Cmp> {
using type = typelist<T...>;
};
// L empty
template <typename... T, typename Cmp>
struct merge<typelist<>, typelist<T...>, Cmp> {
using type = typelist<T...>;
};
然后是递归步骤,有点难看:
template <typename A, typename... As, typename B, typename... Bs, typename Cmp>
struct merge<typelist<A, As...>, typelist<B, Bs...>, Cmp>
: std::conditional<
Cmp::template apply<A, B>::value,
concat_t<typelist<A>, typename merge<typelist<As...>, typelist<B, Bs...>, Cmp>::type>,
concat_t<typelist<B>, typename merge<typelist<A, As...>, typelist<Bs...>, Cmp>::type>
>::type
{ };
基本上,给定两个类型列表,{A, As...}
和 {B, Bs...}
, 我们根据 Cmp
选择最小的,这就是我们要从中弹出元素的一面。如果Cmp::apply<A,B>
, 然后我们连接 A
合并的结果 {As...}
与 {B, Bs...}
.反之亦然。
这就是她写的全部内容:
template <typename T>
struct TD;
int main()
{
using T = sort<typelist<int, double, char, float>, LessSize>::type;
TD<T> r;
}
main.cpp: In function 'int main()':
main.cpp:131:11: error: aggregate 'TD<typelist<char, float, int, double> > r' has incomplete type and cannot be defined
TD<T> r;
^
一旦你有了排序的类型,制作一个元组就很简单了:
template <template <typename...> class C>
struct meta_quote {
template <typename... T>
using apply = C<T...>;
};
template <typename F, typename TL>
struct meta_apply;
template <typename F, typename... T>
struct meta_apply<F, typelist<T...>> {
using type = typename F::template apply<T...>;
};
template <typename... T>
struct my_tuple
: meta_apply<meta_quote<std::tuple>,
typename sort<typelist<T...>>::type
>::type;
{
using base_tuple = meta_apply<...>;
};
现在只需为 get<>
添加重载在 my_tuple<T...>
:
template <size_t I, typename... T>
auto get(my_tuple<T...>& t) {
using type = std::tuple_element_t<I, std::tuple<T...>>;
return std::get<type>(static_cast<typename my_tuple<T...>::base_type&>(t));
}
关于c++ - 按大小存储字段的结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30782529/