【问题标题】:std::distance for iterators worse then RandomAccessIterator迭代器的 std::distance 比 RandomAccessIterator 更差
【发布时间】:2015-11-01 15:51:06
【问题描述】:

我最近回顾了不久前实现的 algorithm 并发现 it can be improved 不仅可以在 RandomAccessIterator 输入范围上进行操作,而且甚至可以在 ForwardIterator 1 上进行操作(多通道保证仍然是要求)。

我决定通过修改代码中的所有位置来简单地降级需求,目前使用迭代器之间的简单差异,以使用std::distance 代替。 std::distancealgorithm 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&lt; iterator &gt; x; 修改为 std::set&lt; iterator, decltype(less) &gt; x{less};

  • 对于算法内部使用的无序容器(value_typeiterator)我使用 std::hash&lt; std::intptr_t &gt; h; 哈希函数进行简单的差异 h(lhs - rhs)(实际上是哈希值的 xor 组合,但它在这里无关紧要)对于一些固定的iterator lhs 和一些变化的iterator rhs。修改为h(std::distance(lhs, rhs))

前一点是正确的,但后一点显然不是。由于rhs 无法从lhs 中恢复,只能通过简单的递增,并且无法先验地知道要做什么:递增lhs 以达到rhs 或递增rhs 以达到lhs.

但上面的哈希仍然有效。这很奇怪......因为有时,当lhs &gt; 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 总是返回非负数。

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

【问题讨论】:

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


    【解决方案1】:

    [...] 是否可以为任意一对ForwardIterator-s 应用std::distance

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

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

    [...] 要求:如果InputIterator满足随机访问迭代器的要求,则last应可从firstfirst应可从last;否则,last 应该可以从first 访问

    [N4431 §24.4.4/5]

    原因是对于前向迭代器,该函数重复应用operator++ 以从first 获取last

    由于只有随机访问迭代器提供 + 和 - 运算符,因此库提供了两个函数模板 advancedistance。这些函数模板将+- 用于随机访问迭代器(因此它们的时间是常数);对于输入、前向和双向迭代器,他们使用++ 来提供线性时间实现。

    [N4431 §24.4.4/1]

    【讨论】:

      【解决方案2】:

      从您链接的文档中,“如果 InputIt 不是 RandomAccessIterator,如果通过(可能重复地)递增 first 无法从 first 访问 last,则行为未定义。

      未定义的行为并不意味着访问冲突;它意味着任何东西,包括返回一些正数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-01-18
        • 1970-01-01
        • 2012-03-26
        • 1970-01-01
        • 1970-01-01
        • 2011-05-13
        • 2012-11-17
        相关资源
        最近更新 更多