std ::迭代器的距离比RandomAccessIterator

时间:2015-08-09 18:38:04

标签: c++ algorithm c++11 stl c++14

我最近审核了不久前实施的algorithm并发现,it can be improved不仅可以在RandomAccessIterator输入范围上运行,还可以在ForwardIterator上运行(多通保证仍然是要求)。

我决定通过修改代码中的所有位置(当前使用迭代器之间的简单差异)来简单地降级需求,而不是使用std::distancestd::distance algorithm described here。由于std::distance算法的本质,修改不仅仅是搜索和替换,即:

  • 对于算法内部使用的已订购容器,我将所有出现的operator <std::less< iterator > / std::less<>替换为:
auto const less = [ibeg = std::cbegin(input)] (iterator const & l, iterator const & r)
                   {
                       return std::distance(ibeg, l) < std::distance(ibeg, r);
                   };

说,std::set< iterator > x;已修改为std::set< iterator, decltype(less) > x{less};

  • 对于算法内部使用的无序容器(value_typeiterator),我使用std::hash< std::intptr_t > h;哈希函数来实现简单差异h(lhs - rhs)(实际上 xor 哈希值的组合,但这里不重要)对于某些固定的iterator lhs和一些变化的iterator rhs。它被修改为h(std::distance(lhs, rhs))

前一点是正确的,但后者显然没有。由于rhs只能通过简单的增量才能从lhs无法恢复,并且无法知道先验地做什么:将lhs增加到达rhs或者将rhs增加到lhs

但上面的哈希仍然有效。这很奇怪......因为有时,lhs > rhs(在某种意义上),应该存在访问冲突(有时)。说,如果:

std::list< int > input{1, 2, 3, 4};
auto a = std::begin(input), b = std::next(lhs, 2);

然后包含在std::distance(b, a)中的增量应该永远不会停止并最终到达尚未分配的内存页面的边界。但AV在我的程序中永远不会发生。 std::distance始终返回非负数。

它是未定义的行为还是允许将std::distance应用于任何一对ForwardIterator-s? -stdlib=libc++

2 个答案:

答案 0 :(得分:2)

  

[......]是否可以将std::distance用于任何一对ForwardIterator - s?

如果你想坚持标准,那么显然不,不允许

template <class InputIterator>
typename iterator_traits<InputIterator>::difference_type distance(
  InputIterator first, InputIterator last);
     

[...]   要求:如果InputIterator符合随机访问迭代器的要求,则last可以访问first,或first可以访问last;否则, last可以从first 到达。

     

[N4431§24.4.4/ 5]

原因是对于前向迭代器,该函数重复应用operator++firstlast

  

由于只有随机访问迭代器提供了+和 - 操作符,因此该库提供了两个函数模板advancedistance。这些函数模板使用+-作为随机访问迭代器(因此,它们是常量时间);对于输入,转发和双向迭代器,它们使用++来提供线性时间实现。

     

[N4431§24.4.4/ 1]

答案 1 :(得分:2)

从您关联的文档&#34;如果InputIt不是RandomAccessIterator,如果last无法从first到达(可能重复),则行为未定义递增first&#34;。

未定义的行为并不意味着访问违规;它意味着什么,包括返回一些正数。