我不想使用std::distance
,因为它会计算从迭代器到终点的整个距离。但是我需要确保从迭代器到最后有 N 个或更多元素。所以我正在使用下一个代码:
if (std::next(it, n) != c.end()) // c is a std::multimap
{
/// my logic
}
一切都很棒,并且与我的编译器(g++ (GCC) 4.8.3 20140911 (Red Hat 4.8.3-9)
)一起工作,但我有疑问。在文档(cpprefenece.com&&cplusplus.com)中,我找不到有关n > std::distance(it , c.end())
或任何其他特殊情况的任何信息。所以。我的代码安全吗?或者我应该写我自己的nextIfPossible
?
distance(it, c.end())
小于n
,则next(it, n)
是未定义的行为。
[C++14: 5.7/5] 如果指针操作数和结果都指向同一数组对象的元素,或者指向数组对象的最后一个元素,则计算不应产生溢出;否则,行为是未定义的。
有关详细信息,请参阅此处:非取消引用迭代器是否超过了数组未定义行为的"一个过去结束"迭代器?
您必须编写nextIfPossible
否则您的代码是未定义的。也就是说,由于我猜这是一个随机访问迭代器,您会发现在必须执行边界检查的情况下,使用索引进行基准测试比使用迭代器更快:https://stackoverflow.com/a/37299761/2642059
所以我建议甚至不要打扰迭代器或nextIfPossible
而只是使用索引并根据大小检查它。
根据标准 §24.4.4/p3 和 p6 迭代器操作 [iterator.operations](强调我的):
因此,template <class InputIterator, class Distance> constexpr void advance(InputIterator& i, Distance n);
2 要求:n 仅对双向和随机为负数 访问迭代器。
3效果:递增(或负 n 的递减) 迭代器引用 i by n。
template <class InputIterator> constexpr InputIterator next(InputIterator x, typename std::iterator_traits<InputIterator>::difference_type n = 1);
6效果:相当于:
advance(x, n); return x;
没有边界检查,因此如果输入n
大于 std::distance(it , c.end())
,则可能会导致未定义的行为。
libstd++
中RB_Tree::iterator
增量运算符的实现可确保它不会将迭代器返回到未定义的内存位置:
static _Rb_tree_node_base*
local_Rb_tree_increment(_Rb_tree_node_base* __x) throw ()
{
if (__x->_M_right != 0)
{
__x = __x->_M_right;
while (__x->_M_left != 0)
__x = __x->_M_left;
}
else
{
_Rb_tree_node_base* __y = __x->_M_parent;
while (__x == __y->_M_right)
{
__x = __y;
__y = __y->_M_parent;
}
if (__x->_M_right != __y)
__x = __y;
}
return __x;
}
但对于std::vector
来说并非如此:
#include <iostream>
#include <vector>
#include <iterator>
int main() {
std::vector<int> vec = {1,2};
auto it = vec.begin();
it = std::next(it, 5);
if (it != vec.end()) {
std::cout << "Not end..go on" << std::endl;
}
return 0;
}
这将继续打印消息。
因此,由于容器之间的行为并不相同,因此对于基于 map
的容器,您不应依赖 std::next
来实现其当前正确行为。