代码之家  ›  专栏  ›  技术社区  ›  SCFrench

在构造时将C++迭代器范围连接到const向量成员变量

  •  7
  • SCFrench  · 技术社区  · 15 年前

    我有一个类X,我在这里提供了一个片段:

    class X {
      public:
        template <typename Iter>
        X(Iter begin, Iter end) : mVec(begin, end) {}
    
      private:
        vector<Y> const mVec;
    };
    

    现在我想向这个类添加一个新的连接构造函数,如下所示:

    template <typename Iter1, typename Iter2>
    X(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) : mVec(???) { ??? }
    

    这样的构造器将把两个范围[开始1,结束1]和[开始2,结束2]关联到MVEC中。挑战是

    1)我想保留mvec上的const,以便在x的其他方法中被认为是常量。

    2)尽可能避免不必要的复印件。也就是说,一种解决方案是使用一个静态方法来构造范围1的临时非常量,插入范围2并返回它,然后将连接构造函数定义为

    template <typename Iter1, typename Iter2>
    X(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) 
      : mVec(concatenate(begin1, end1, begin2, end2)) { }
    

    但我相信,这至少会额外复制一次所有值。

    5 回复  |  直到 15 年前
        1
  •  9
  •   David Rodríguez - dribeas    15 年前

    好问题。我将尝试实现一个特定的迭代器包装器类型,它将两个范围转换为一个范围。行中的某些内容:

    // compacted syntax for brevity...
    template <typename T1, typename T2>
    struct concat_iterator
    {
    public:
       typedef std::forward_iterator_tag iterator_category;
       typedef typename iterator_traits<T1>::value_type value_type;
       typedef *value_type pointer; 
       typedef &value_type reference;
    
       concat_iterator( T1 b1, T1 e1, T2 b2, T2 e2 ) 
          : seq1( b1 ), seq1end( e1 ), seq2( b2 ), seq2end( e2 );
       iterator& operator++() {
          if ( seq1 != seq1end ) ++seq1;
          else ++seq2;
          return this;
       }
       reference operator*() {
          if ( seq1 != seq1end ) return *seq1;
          else return *seq2;
       }
       pointer operator->() {
          if ( seq1 != seq1end ) return &(*seq1);
          else return &(*seq2);
       }
       bool operator==( concat_iterator const & rhs ) {
          return seq1==rhs.seq1 && seq1end==rhs.seq2 
              && seq2==rhs.seq2 && seq2end==rhs.seq2end;
       }
       bool operator!=( contact_iterator const & rhs ) {
          return !(*this == rhs);
       }
    private:
       T1 seq1;
       T1 seq1end;
       T2 seq2;
       T2 seq2end;
    };
    
    template <typename T1, typename T2>
    concat_iterator<T1,T2> concat_begin( T1 b1, T1 e1, T2 b2, T2 e2 )
    {
       return concat_iterator<T1,T2>(b1,e1,b2,e2);
    }
    template <typename T1, typename T2>
    concat_iterator<T1,T2> concat_end( T1 b1, T1 e1, T2 b2, T2 e2 )
    {
       return concat_iterator<T1,T2>(e1,e1,e2,e2);
    }
    

    现在您可以使用:

     class X {
     public:
        template <typename Iter, typename Iter2>
        X(Iter b1, Iter e1, Iter2 b2, Iter2 e2 ) 
          : mVec( concat_begin(b1,e1,b2,e2), concat_end(b1,e1,b2,e2) ) 
        {}
    
      private:
        vector<Y> const mVec;
    };
    

    或者(我刚刚想到了)您不需要重新声明构造函数。使调用者使用助手函数:

    X x( concat_begin(b1,e1,b2,e2), concat_end(b1,e1,b2,e2) );
    

    我没有检查代码,只是在我的头顶上打了个字。它可以编译也可以不编译,可以工作也可以不工作…但你可以把它作为一个起点。

        2
  •  2
  •   avakar    15 年前

    最好还是放弃 const (你为什么要坚持呢?).

    否则,您必须构建一个连接迭代器。这是相当多的代码,看 this thread 更多。

        3
  •  2
  •   Mark Ransom    15 年前

    C++的最好或最坏的特性之一,取决于你的观点,你可以在必要时滥用它来完成任务。在这种情况下,警察是受害者:

    template <typename Iter1, typename Iter2>
    X(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2) : mVec(begin1, end1) {
        const_cast<vector<Y>&>(mVec).insert(mVec.end(), begin2, end2);
    }
    

    我可能把一些细节搞错了,我没有试着编译这个。但它应该给你这个主意。

        4
  •  1
  •   Salman A    15 年前

    静态方法可能没有您想象的那么糟糕,这取决于编译器所做的优化。在C++0x中,移动构造函数将删除当前正在发生的任何复制。

    同时使用包装迭代器。代码不太可能像avakar链接到的线程那样糟糕,因为您只需要实现 input iterator .

        5
  •  1
  •   dirkgently    15 年前

    1)我想保留mvec上的const,以便在x的其他方法中被认为是常量。

    • 这是一种奇怪的用法 const 在成员变量上。而且它不符合好的设计。根据定义,构造是一个需要更改对象的过程。

    • 至于您保持对象不可修改的需求——使用适当的封装。你应该使用 康斯特 -成员函数以公开基于 mVec 为你们班的客户服务。

    2)尽可能避免不必要的复印件。也就是说,一种解决方案是使用一个静态方法来构造范围1的临时非常量,插入范围2并返回它,然后将连接构造函数定义为

    您应该查看移动构造函数和R-值引用(通常是C++ 0x的承诺目标)。读这个 article .