7

我有一个 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(???) { ??? }

这样的构造函数会将两个范围 [begin1, end1) 和 [begin2, end2) 连接到 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)) { }

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

4

5 回答 5

9

好问题。我会尝试实现一个特定的迭代器包装类型,将两个范围变成一个范围。类似的东西:

// 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) );

我没有检查代码,只是在这里输入了我的头顶。它可以编译也可以不编译,它可以工作也可以不工作……但你可以以此为起点。

于 2009-04-16T18:02:09.777 回答
2

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

否则,您必须构建一个连接迭代器。这是相当多的代码,请参阅此线程了解更多信息。

于 2009-04-16T17:29:35.517 回答
2

根据您的观点,C++ 最好或最差的特性之一是您可以在必要时滥用它来完成工作。在这种情况下, const_cast 是受害者:

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);
}

我可能有一些细节错误,我没有尝试编译它。但它应该给你的想法。

于 2009-04-16T17:49:33.923 回答
1

1)我想保留 mVec 上的 const,以便它在 X 的其他方法中被认为是恒定的。

  • 这是const对成员变量的一种奇怪用法。它违背了良好的设计。根据定义,构造是一个需要对象发生变化的过程。

  • 至于您保持对象不可修改的要求 - 使用适当的封装。您应该使用-member 函数来为您的类的客户const公开任何基于您的功能。mVec

2)如果可能的话,我想避免不必要的副本。也就是说,一种解决方案是有一个静态方法,该方法构造一个非常量临时到范围 1,插入范围 2 并返回它,然后定义连接构造函数以

您应该总体上查看移动构造函数和 r 值引用(C++0x 的承诺目标)。阅读这篇文章

于 2009-04-16T19:21:06.420 回答
1

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

同时使用包装迭代器。代码不会像 avakar 链接到的线程那么糟糕,因为您只需要实现一个输入迭代器

于 2009-04-16T18:03:19.413 回答