“结束()”后置插入器的迭代器?
对于诸如从 std::back_inserter() 返回的迭代器,是否有可以用作“结束”迭代器的东西?
乍一看这似乎有点无意义,但我有一个 API,它是:
template<typename InputIterator, typename OutputIterator>
void foo(
InputIterator input_begin,
InputIterator input_end,
OutputIterator output_begin,
OutputIterator output_end
);
foo
对输入序列执行一些操作,生成输出序列。 (foo
知道 Who 的长度,但可能等于也可能不等于输入序列的长度。)
output_end
参数的取值是奇数部分:std例如,::copy
不会这样做,并且假设您不会向它传递垃圾。 foo
这样做是为了提供范围检查:如果您传递的范围太小,它就会以防御性编程的名义抛出异常。 (而不是潜在地覆盖内存中的随机位。)
现在,假设我想传递 foo
一个后插入器,特别是来自 std::vector
的插入器,它在外部没有限制的内存限制。我仍然需要一个“结束”迭代器 - 在这种情况下,它永远不会比较相等。 (或者,如果我有一个 std::vector
但长度有限制,也许它有时可能比较相等?)
我该如何去做呢?我确实有能力更改 foo
的 API - 是否最好不检查范围,而是提供替代方法来获取所需的输出范围? (对于原始数组来说无论如何都是需要的,但对于向后插入到向量中则不需要。)这看起来不太健壮,但我正在努力使“健壮”(上面)工作。
For iterators such as those returned from std::back_inserter()
, is there something that can be used as an "end" iterator?
This seems a little nonsensical at first, but I have an API which is:
template<typename InputIterator, typename OutputIterator>
void foo(
InputIterator input_begin,
InputIterator input_end,
OutputIterator output_begin,
OutputIterator output_end
);
foo
performs some operation on the input sequence, generating an output sequence. (Who's length is known to foo
but may or may not be equal to the input sequence's length.)
The taking of the output_end
parameter is the odd part: std::copy
doesn't do this, for example, and assumes you're not going to pass it garbage. foo
does it to provide range checking: if you pass a range too small, it throws an exception, in the name of defensive programming. (Instead of potentially overwriting random bits in memory.)
Now, say I want to pass foo
a back inserter, specifically one from a std::vector
which has no limit outside of memory constraints. I still need a "end" iterator - in this case, something that will never compare equal. (Or, if I had a std::vector
but with a restriction on length, perhaps it might sometimes compare equal?)
How do I go about doing this? I do have the ability to change foo
's API - is it better to not check the range, and instead provide an alternate means to get the required output range? (Which would be needed anyways for raw arrays, but not required for back inserters into a vector.) This would seem less robust, but I'm struggling to make the "robust" (above) work.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
正如我在对 j_random_hacker 的回答的评论中提到的,例如,如果您修改算法,使得传递给它的迭代器可以是不同类型,那么
您可以非常轻松地编写一个
back_inserter_end
类来测试:然后你可以调用你的“安全”算法:
在你的“安全”算法中,你可以在使用
out_it
之前测试是否out_it == out_end
;由于out_it
是一个back_insert_iterator
,因此此测试将始终返回false
(这是所需的行为)。As I mentioned in a comment to j_random_hacker's answer, if you modify the algorithm such that the iterators passed to it can be of different types, for example,
then you can very easily write a
back_inserter_end
class to test against:You can then call your "safe" algorithm:
Inside of your "safe" algorithm, you can test whether
out_it == out_end
before usingout_it
; sinceout_it
is aback_insert_iterator
, this test will always returnfalse
(which is the desired behavior).如果
foo
正在检查以确保distance(output_begin, output_end)
足够大以包含结果,那么您可以使用什么作为“end”迭代器?back_inserter
将元素添加到末尾;根据定义,back_inserter
添加元素的位置与序列末尾之间的距离
为0
。在我看来,带有
std::copy
类似签名foo(InIt, InIt, OutIt)
的foo
是你最好的选择。它并不是真正的“不健壮”。对于大多数算法,出于性能原因,您只想在调试版本中进行这种范围检查,并且一个不错的标准库实现(如 Visual C++ 标准库)已经在调试版本中提供了大量范围检查。或者,您可以创建一个
back_inserting_foo(InIt, InIt, Container)
,尽管为此创建一个特殊情况会有点不寻常,并且会给函数的用户带来更大的负担,让他们无法知道他们需要哪个重载用于不同类型的迭代器。If
foo
is checking to ensure thatdistance(output_begin, output_end)
is sufficiently large to contain the results, what could you use as an "end" iterator? Aback_inserter
adds elements to the end; thedistance
between the place at which aback_inserter
adds elements and the end of the sequence is, by definition,0
.A
foo
with thestd::copy
-like signaturefoo(InIt, InIt, OutIt)
is, in my opinion, your best option. It isn't really "not robust." For most algorithms, you only want to do this kind of range checking in debug builds for performance reasons, and a decent Standard Library implementation (like the Visual C++ Standard Library) will already provide a lot of range checking in debug builds.Alternatively, you could create a
back_inserting_foo(InIt, InIt, Container)
, though creating a special case for this would be a bit unusual and places a greater burden upon users of the function to know which overload they need to use for different types of iterators.您可以通过在写出每个元素之前执行安全检查来避免更改
foo()
的 API(请参见下文) ,而不是在开始时使用distance()
检查 詹姆斯·麦克内利斯建议。执行此操作需要编写自己的“迭代器适配器”以提供“增强型”输出迭代器,该迭代器可以测试它是否位于有效范围的末尾。然后,您可以正确地将模板类型名称从
OutputIterator
更改为SafeOutputIterator
—— 即使这只是作为文档,因为它无法在 C++ 中强制执行。我们区分“有界”和“无界”迭代器对:对于后者,没有两个迭代器会比较相等,事实上,除了其类型之外,任何其他迭代器都不需要第二个迭代器。您现在可以编写如下代码:
您可以在每次写入
*curr_output_iter
之前让foo()
检查curr_output_iter != output_end
,或者插入safe_output_iterator::operator=()
中的检查。后者似乎更可取,因为每当一对safe_output_iterator
被传递给任何任意算法时,您就不会忘记执行检查(大概这类似于 STL 的调试版本的工作方式)。您还可以为固定大小的数组编写soi_begin_cont()
和soi_end_cont()
的重载。如果 ranges 是这样的话,所有这些都会变得不那么麻烦。由 STL 算法而不是迭代器对使用——例如,这样单个函数模板就可以返回跨越整个数组的适当范围。
You can avoid changing
foo()
's API by performing the safety check as you go, by checking thatcurr_output_iter != output_end
before each element is written out (see below), instead of once at the start with adistance()
check as James McNellis suggests.Doing this will require writing your own "iterator adaptor" to provide an "enhanced" output iterator that can test for whether it's at the end of its valid range. Then you would rightfully change the template typenames from
OutputIterator
toSafeOutputIterator
-- even though this just serves as documentation because it's something that can't be enforced in C++. We distinguish between "bounded" and "unbounded" iterator pairs: for the latter, no two iterators will ever compare equal, and in fact the 2nd iterator will never be needed for anything besides its type.You can now write code like:
You can either have
foo()
checkcurr_output_iter != output_end
before each write through*curr_output_iter
, or alternatively insert the check insafe_output_iterator::operator=()
. The latter seems preferable as you then cannot forget to perform the check whenever a pair ofsafe_output_iterator
s are passed to any arbitrary algorithm (presumably this is similar to how debug versions of the STL work). You could also write overloads ofsoi_begin_cont()
andsoi_end_cont()
for fixed-size arrays.All this would be much less cumbersome if ranges were used by STL algorithms instead of iterator pairs -- that way a single function template could return an appropriate range spanning an entire array, for example.