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

发布于 2024-07-16 14:54:04 字数 813 浏览 3 评论 0原文

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

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

I have a class X, which I provide a snippet of here:

class X {
  public:
    template <typename Iter>
    X(Iter begin, Iter end) : mVec(begin, end) {}

  private:
    vector<Y> const mVec;
};

I now want to add a new concatenating constructor to this class, something like:

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

Such a constructor would catenate the two ranges [begin1, end1) and [begin2, end2) into mVec. The challenges are

1) I would like to preserve the const on mVec, so that it is considered constant throughout the other methods of X.

2) I would like to avoid unnecessary copies if at all possible. That is, one solution is to have a static method that constructs a non-const temporary to range 1, inserts range 2 and returns it, and then define the concatenating constructor to

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

but that copies all the values at least one extra time, I believe.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

十年不长 2024-07-23 14:54:04

好问题。 我会尝试实现一个特定的迭代器包装类型,将两个范围转换为单个范围。 内容如下:

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

我还没有检查代码,只是凭空在此处输入它。 它可以编译或不能编译,它可以工作或不工作......但你可以以此为起点。

Nice problem. I would try to implement a particular iterator wrapper type that turns the two ranges into a single range. Something in the lines of:

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

Now you could use:

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

or (I have just thought of it) you don't need to redeclare your constructor. Make your caller use the helper functions:

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

I have not checked the code, just typed it here off the top of my head. It could compile or it could not, it could work or not... but you can take this as a start point.

猫弦 2024-07-23 14:54:04

最好放弃 const (为什么你还要坚持使用它?)。

否则,您必须构建一个串联迭代器。 代码相当多,请参阅此线程了解更多的。

It would probably be best to drop const (why would you insist on it anyway?).

Otherwise, you have to build a concatenating iterator. It is quite a lot of code, see this thread for more.

勿忘初心 2024-07-23 14:54:04

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

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

One of the best or worst features of C++, depending on your viewpoint, is that you can abuse it when necessary to get the job done. In this case, const_cast is the victim:

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

I might have some of the details wrong, I didn't try to compile this. But it should give you the idea.

溺渁∝ 2024-07-23 14:54:04

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

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

Your static method might not be as bad as you think, depending on the optimization your compiler does. And in C++0x, move constructors will remove any copying that is currently taking place.

In the meantime go with a wrapper iterator. The code is not likely to be as bad as the thread avakar links to, since you only need to implement an input iterator.

无悔心 2024-07-23 14:54:04

1) 我想保留 mVec 上的 const,以便在 X 的其他方法中将其视为常量。

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

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

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

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

1) I would like to preserve the const on mVec, so that it is considered constant throughout the other methods of X.

  • This is a curious use of const on a member variable. And it defies good design. By definition, construction is a process which requires the object to change.

  • As for your requirement to keep the object non-modifiable -- use proper encapsulation. You should use const-member functions to expose any functionality based on your mVec for the clients of your class.

2) I would like to avoid unnecessary copies if at all possible. That is, one solution is to have a static method that constructs a non-const temporary to range 1, inserts range 2 and returns it, and then define the concatenating constructor to

You should be looking at move-constructors and r-value references in general (a promised goal of C++0x). Read this article.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文