将 STL 算法限制为 N 个元素
(受到 nakiya 评论的启发)
许多 STL 算法将一个范围作为一对迭代器。例如,for_each(begin, end, &foo);
。显然,如果 distance(begin, end) >= N
,并且 begin 是一个随机访问迭代器,则 for_each(begin, begin+N, &foo);
仅将 foo
应用于前 N 个元素。
如果这两个条件中的任何一个不满足,现在是否有一个干净、通用的替代方案?
(Inspired by a comment from nakiya)
Many STL algorithms take a range as a pair of iterators. For instance, for_each(begin, end, &foo);
. Obviously, if distance(begin, end) >= N
, and begin is a random-access iterator, then for_each(begin, begin+N, &foo);
applies foo
only to the first N elements.
Now is there a clean, generic alternative if either of these two conditions is not met?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在不改变迭代器类型的情况下,不存在通用的完整解决方案。
证明:假设迭代器类型只是一个InputIterator,因此
begin
实际上引用(例如)一个流,而end
是一个特殊情况的标记迭代器,它将一旦真实迭代器读取了 EOF,就与“真实”迭代器进行比较。然后,任何使用
begin
尝试计算出end
的新值以传递给算法的操作,都将“消耗”begin
的原始值>,因为这就是 InputIterator 的工作原理。您可以做的是编写一个迭代器包装类,以便迭代器计算它已递增的次数,并在递增 N 次后与“结束”迭代器进行比较。 N 可以是模板参数,也可以是一个或另一个迭代器的构造函数参数。
像这样的东西。我已经测试过它可以编译并且适合我。仍有待完成 - 我目前只处理你的两种情况之一,“不是随机访问迭代器”。我也不处理另一个“距离
请注意,第二个构造函数仅采用迭代器,因为基础类型可能无法默认构造(如果它只是一个 InputIterator)。在调用者创建“结束”迭代器的情况下,它不会使用它,因为一旦另一个副本递增,它就不再有效。
如果底层迭代器类型是 RandomAccess,则不需要/不需要此包装器。因此,我提供了一个辅助模板函数,它以与
back_inserter
为back_insert_iterator
执行相同的方式进行类型推导。但是,如果其参数类型是随机访问类别的迭代器,则帮助程序不应返回FiniteIterator
,而应返回T
:示例用法 (和最小测试):
警告:
finite_iterator(x, 1) == Finite_iterator(++x, 0)
为 false,即使对于前向迭代器或更好的迭代器也是如此。有限迭代器仅在从相同起点创建时才具有可比性。此外,这还没有完成。例如,
std::reverse
不起作用,因为为了访问引用数,finite_iterator(x, 1)
“指向”x。目前,以下情况正在起作用:
所以我离得不远,但这不是一个好的界面。我需要更多地考虑双向迭代器的情况。
There is no generic full solution without changing the iterator type.
Proof: suppose that the iterator type is only an InputIterator, so
begin
actually refers to (for example) a stream, andend
is a special-case marker iterator, which will compare equal to the "real" iterator once the real iterator has read EOF.Then any use of
begin
to try to work out a new value ofend
to pass to the algorithm, will "consume" the original value ofbegin
, since that's how InputIterators work.What you could do is write an iterator wrapper class, such that the iterator counts how many times it has been incremented, and compares equal to an "end" iterator once it has been incremented N times. N could be a template parameter, or a constructor parameter to one or other of the iterators.
Something like this. I've tested it compiles and works for me. Still to do - I'm currently only handling one of your two situations, "not a random-access iterator". I don't also handle the other, "distance < N".
Note that the second constructor only takes an iterator because the underlying type might not be default constructible (if it's only an InputIterator). In the case where the caller is creating an "end" iterator it doesn't use it, because it won't be valid once the other copy is incremented.
If the underlying iterator type is RandomAccess, then this wrapper isn't needed/wanted. So I provide a helper template function, that does the type deduction the same way
back_inserter
does forback_insert_iterator
. However, in the case where its parameter type is an iterator of random-access category, the helper shouldn't returnFiniteIterator<T>
, but justT
:Example usage (and minimal test):
Caution:
finite_iterator(x, 1) == finite_iterator(++x, 0)
is false, even for a forward iterator or better. Finite iterators are only comparable if they are created from the same starting point.Also, this still isn't complete. For example
std::reverse
doesn't work, because for the purposes of accessing the referand,finite_iterator(x, 1)
is "pointing at" x.Currently the following happens to work:
So I'm not far off, but that's not a good interface. I would need to think more about the case of Bidirectional iterators.
已经有 fill_n 和generate_n,没有 foreach_n (或者 for_n 可能更合适),但是编写一个很容易。
您可以执行 op(*begin++) ,但尽管输入较少,但可能会生成更多代码来复制迭代器。 size_type 是数字,因此进行后递增的效率并不低,在这种情况下它很有用。
There is already fill_n and generate_n, there is no foreach_n (or for_n would probably be more appropriate) but it is easy enough to write one.
You could do op(*begin++) but although it is less typing it may generate more code to copy the iterator. size_type is numeric so doing post-increment is no less efficient and here is a case where it is useful.
我相信您可以创建一个类似于 boost::counting_iterator 的包装迭代器类型,它会将增量和底层迭代器保持在一起,并且一旦增量超过,就会与“结束”迭代器进行比较最大值。
I believe you could create a wrapper iterator type similar to
boost::counting_iterator
which would keep together both an increment and the underlying iterator, and would compare equal to an "end" iterator as soon as the increment exceeds the maximum value.