从C ++ 11可变参数模板参数中消除重复的条目 [英] Eliminate duplicate entries from C++11 variadic template arguments
问题描述
我在C ++中使用具有多个虚拟继承的可变参数模板将类型聚合为单个结构定义.
I'm using variadic templates with multiple virtual inheritance in C++ to aggregate types into a single structure definition.
这是一组结构示例:
struct meas { int i; };
struct meas2 : public virtual meas { int j; };
struct meas3 : public virtual meas { int k; };
然后,我使用多个虚拟继承来聚合它们:
I then aggregate these using multiple virtual inheritance:
template <typename... Args>
struct zipper : public virtual Args... {};
然后我可以这样做:
typedef zipper<meas, meas2> meas_type;
meas* m = new meas_type;
然后可以层叠:
typedef zipper<meas3, meas_type> meas_type2;
但是,生成的对象相当笨拙:
The resulting object, however, is rather unwieldy:
$46 = (zipper<meas3, zipper<meas, meas2> >) {
<meas3> = {
<meas> = {
i = 0
},
members of meas3:
_vptr.meas3 = 0x400ec8,
k = 0
},
<zipper<meas, meas2>> = {
<meas2> = {
members of meas2:
_vptr.meas2 = 0x400ee0,
j = 6299120
},
members of zipper<meas, meas2>:
_vptr.zipper = 0x400eb0
}, <No data fields>}
根据gdb.
尝试压缩相同的基本类型时,还有一个次要问题:
There is also a secondary problem when attempting to zip the same base type:
typedef zipper<meas, meas> meas_type2;
上面在G ++ 4.6.3下产生了编译器错误重复的基类'meas'无效".
The above produces the compiler error "duplicate base class 'meas' is invalid" under G++ 4.6.3.
问题因此是双重的:
- 是否可以将
zipper<meas3, zipper<meas, meas2>>
转换为zipper<meas3, meas2>
? - 在完成#1时是否有办法删除类型列表中的重复条目?
- Is there a way to transform
zipper<meas3, zipper<meas, meas2>>
intozipper<meas3, meas2>
? - Is there a way, while accomplishing #1, to remove duplicate entries in the type list?
谢谢!
推荐答案
我解决此问题的策略是使用一些间接级别.
My strategy for solving this problem is to use a few levels of indirection.
- 拉链< Args ...>通过继承将其参数的处理分派给函数process_zipper_arguments:
示例:
template < typename... Args >
struct zipper : zipper < typename process_zipper_arguments < Args... >::type > {};
- 使用
template < typename... Args > struct typelist {}
跟踪要继承的对象类型. - 专门化
struct zipper < typelist < Args... > >: public virtual Args...
进行实际继承 - use a
template < typename... Args > struct typelist {}
to keep track of the object types from which you want to inherit. - Specialize
struct zipper < typelist < Args... > >: public virtual Args...
to do the actual inheritance -
is_in < CandidateType, typelist< Args... > >::type
是true_type
或false_type
,可以递归定义 -
add_unique < CandidateType, typelist< Args... > >::type
是已添加或未添加CandidateType的typelist <...>
.它会调用is_in
来确定. is_in < CandidateType, typelist< Args... > >::type
is eithertrue_type
orfalse_type
and can be defined recursivelyadd_unique < CandidateType, typelist< Args... > >::type
is atypelist <...>
that has either CandidateType added to it or not. It callsis_in
to determine that.
为了摆脱重复的父类型,在process_zipper_arguments
中使用了两个辅助函数:
In order to get rid of duplicate parent types, two helper functions are used in process_zipper_arguments
:
这是完整的代码,至少可以使用带有--std = c ++ 0x的g ++(GCC)4.6.3进行编译.欢迎对此进行批评.
Here is the complete code that compiles at least with g++ (GCC) 4.6.3 with --std=c++0x. Criticism on it is welcome.
// Forward declarations
template < typename... Args >
struct zipper;
// Two types meaning true and false
struct true_type {};
struct false_type {};
// The only purpose of this struct is to be associated with Types...
template < typename... Types >
struct typelist {};
// ===================================================
// is_in < type, typelist<...> >::type
// is true_type if type is in typelist
// is false_type if type is not in typelist
// Assume TElement is not in the list unless proven otherwise
template < typename TElement, typename TList >
struct is_in {
typedef false_type type;
};
// If it matches the first type, it is definitely in the list
template < typename TElement, typename... TTail >
struct is_in < TElement, typelist < TElement, TTail... > >
{
typedef true_type type;
};
// If it is not the first element, check the remaining list
template < typename TElement, typename THead, typename... TTail >
struct is_in < TElement, typelist < THead, TTail... > >
{
typedef typename is_in < TElement, typelist < TTail... > >::type type;
};
// ===================================================
// add_unique < TNew, typelist<...> >::type
// is typelist < TNew, ... > if TNew is not already in the list
// is typelist <...> otherwise
// Append a type to a type_list unless it already exists
template < typename TNew, typename TList,
typename Tis_duplicate = typename is_in < TNew, TList >::type
>
struct add_unique;
// If TNew is in the list, return the list unmodified
template < typename TNew, typename... TList >
struct add_unique < TNew, typelist < TList... >, true_type >
{
typedef typelist < TList... > type;
};
// If TNew is not in the list, append it
template < typename TNew, typename... TList >
struct add_unique < TNew, typelist < TList... >, false_type >
{
typedef typelist < TNew, TList... > type;
};
// ===================================================
// process_zipper_arguments < Args... >::type
// returns a typelist of types to be inherited from.
//
// It performs the following actions:
// a) Unpack zipper<...> and typelist <...> arguments
// b) Ignore values that are already in the list
template < typename... Args >
struct process_zipper_arguments;
// Unpack a zipper in the first argument
template < typename... ZipperArgs, typename... Args >
struct process_zipper_arguments < zipper < ZipperArgs... >, Args... >
{
typedef typename process_zipper_arguments < ZipperArgs..., Args... >::type type;
};
// Unpack a typelist in the first argument
template < typename... TypeListArgs, typename... Args >
struct process_zipper_arguments < typelist < TypeListArgs... >, Args... >
{
typedef typename process_zipper_arguments < TypeListArgs..., Args... >::type type;
};
// End the recursion if the list is empty
template < >
struct process_zipper_arguments < >
{
typedef typelist < > type;
};
// Construct the list of unique types by appending them one by one
template < typename THead, typename... TTail >
struct process_zipper_arguments < THead, TTail... >
{
typedef typename
add_unique < THead,
typename process_zipper_arguments < TTail... >::type
>::type type;
};
// ===================================================
// The zipper class that you might want
// If the list of types is not yet known, process it.
// The inheritance is ugly, but there is a workaround
template < typename... Args >
struct zipper : zipper < typename process_zipper_arguments < Args... >::type >
{
// // Instead of inheriting, you can use zipper as a factory.
// // So this:
// typedef zipper < meas2, zipper < meas1, meas > > mymeas;
// // Turns to:
// typedef typename zipper < meas2, zipper < meas1, meas > >::type mymeas;
typedef zipper < typename process_zipper_arguments < Args... >::type > type;
};
// If the list of types is known, inherit from each type
template < typename... Args >
struct zipper < typelist < Args... > >
: public virtual Args...
{};
// ===================================================
// Short usage demo, replace with your own code
struct meas {
int i;
};
struct meas2 {
int j;
};
struct meas3 {
int k;
};
typedef zipper < meas, meas, meas3 > meas_type;
typedef zipper < meas2, meas_type, meas2 > meas_type2;
typedef typename zipper < meas_type2 >::type nicer_meas_type2;
int main ( int, char** )
{
meas * m = new meas_type2;
meas_type2 n;
nicer_meas_type2 o;
return 0;
}
调试它会得到以下结果(return 0;
行的断点):
Debugging it gives the following result (breakpoint at the return 0;
line):
(gdb) print *m
$1 = {i = 0}
(gdb) print n
$2 = {<zipper<typelist<meas, meas3, meas2> >> = {<meas> = {i = 4196320}, <meas3> = {k = 0}, <meas2> = {j = 0},
_vptr.zipper = 0x400928}, <No data fields>}
(gdb) print o
$3 = {<meas> = {i = 4195719}, <meas3> = {k = 0}, <meas2> = {j = 1}, _vptr.zipper = 0x4009a8 <VTT for zipper<typelist<meas, meas3, meas2> >>}
这篇关于从C ++ 11可变参数模板参数中消除重复的条目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!