我编写了一些代码来获取迭代器,但必须按相反的顺序进行比较,
template<class ConstBiIter>
bool func(ConstBiIter seq_begin, ConstBiIter seq_end)
{
ConstBiIter last = std::prev(seq_end);
while (--last != std::prev(seq_begin)) // --> I need to compare the beginning data
{
......
}
return true;
}
在VS2013中,当在调试模式下运行时,--last != std::prev(seq_begin)
将导致调试器断言失败,并显示错误消息
Expression:string iterator + offset out of range.
但在发布模式下运行并给出正确的结果时完全没问题,因为在发布模式下没有边界检查。
我的问题是:
使用std::prev(some_container.begin())
像some_container.rend()
这样的哨兵是否安全?
如何直接将reverse_iterator
与iterator
进行比较?如果我写代码:
std::cout << (std::prev(some_container.begin())==some_container.rend()) << std::endl;
即使你reinterpret_cast
,它也不会编译。
我很好奇prev(some_container.begin())
在物理上等于some_container.rend()
吗?
答案 0 :(得分:9)
不,尝试减少开始迭代器是不安全的。
std::reverse_iterator
(这是std::rend
返回的内容)实际上并没有在开头迭代器之前包含迭代器。它将底层迭代器存储到它从概念上指向的下一个元素。因此,当反向迭代器是&#34;一个超过结束&#34; (即&#34;在开始之前&#34;)它的底层迭代器(通过调用base()
获得)是开始迭代器。
答案 1 :(得分:2)
未定义的行为不安全,即使它今天在您的测试中有效。在C ++中,&#34;它在我尝试时起作用了#34;并不是你正确地做到这一点的好证据:最常见的未定义行为类型之一就是&#34;它似乎起作用了#34;。
问题是未定义的行为工作从根本上说是脆弱的。如果你用力呼吸就会破裂。
编译器可以自由地优化仅通过未定义的行为到达的分支和代码,并且在许多情况下就是这样。在服务补丁,编译器升级,传递给编译器的标志看似无关的变化或可执行路径名的长度之后,它甚至可以自由地这样做。它可以在99.9%的tge时间内自由工作,然后在0.1%的时间内对其他硬盘进行格式化。
其中一些比其他人更有可能。
虽然std::string
和std::vector
元素的迭代器基本上都是发布中的指针,但编译器甚至可以键入一个指向迭代器的指针,即使下一个编译器版本使用包装指针时该假设也会失败
未定义的行为保留在C ++标准中,以允许编译器编写者自由地生成更优的代码。如果你调用它,你可以踩他们的脚趾。
话虽如此,有理由使用C ++标准未定义的行为。执行此操作时,请对其进行大量记录,隔离它,并确保支付(例如,委托速度是std::function
的两倍)是值得的。
以上是非孤立的,不值得做未定义的行为,特别是因为你可以在没有未定义的行为的情况下解决它。
如果您想要向后迭代,最简单的解决方案是制作一些反向迭代器。
template<class ConstBiIter>
bool func(ConstBiIter seq_begin, ConstBiIter seq_end)
{
std::reverse_iterator<ConstBiIter> const rend(seq_beg);
for (std::reverse_iterator<ConstBiIter> rit(seq_end); rit != rend; ++rit)
{
......
}
return true;
}
现在rfirst
向后迭代范围。
如果你需要回到因任何原因而引用同一元素的前向迭代器,而你不是rend
,你可以std::prev(rit.base())
。如果此时rit == seq_end
,那就是未定义的行为。
答案 2 :(得分:1)
24.5.1反向迭代器
类模板reverse_iterator是一个迭代器适配器,它从其底层迭代器定义的序列的末尾迭代到该序列的开头。反向迭代器与其对应的迭代器之间的基本关系由身份建立:&amp; *(reverse_iterator(i))==&amp; *(i - 1)。