从C ++ 11可变参数模板参数中消除重复的条目 [英] Eliminate duplicate entries from C++11 variadic template arguments

查看:68
本文介绍了从C ++ 11可变参数模板参数中消除重复的条目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在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.

问题因此是双重的:

  1. 是否可以将zipper<meas3, zipper<meas, meas2>>转换为zipper<meas3, meas2>?
  2. 在完成#1时是否有办法删除类型列表中的重复条目?
  1. Is there a way to transform zipper<meas3, zipper<meas, meas2>> into zipper<meas3, meas2>?
  2. 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
    • 为了摆脱重复的父类型,在process_zipper_arguments中使用了两个辅助函数:

      In order to get rid of duplicate parent types, two helper functions are used in process_zipper_arguments:

      • is_in < CandidateType, typelist< Args... > >::typetrue_typefalse_type,可以递归定义
      • add_unique < CandidateType, typelist< Args... > >::type是已添加或未添加CandidateType的typelist <...>.它会调用is_in来确定.
      • is_in < CandidateType, typelist< Args... > >::type is either true_type or false_type and can be defined recursively
      • add_unique < CandidateType, typelist< Args... > >::type is a typelist <...> that has either CandidateType added to it or not. It calls is_in to determine that.

      这是完整的代码,至少可以使用带有--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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆