【问题标题】:Decrementing an off the end iterator递减结束迭代器
【发布时间】:2012-08-16 22:15:42
【问题描述】:

我今天正在阅读关于支持双向迭代的容器如何,这段代码是有效的:

Collection c(10, 10);
auto last = --c.end();
*last;

这让我想到,当提交一对双向迭代器 [beg, end) 到 STL 中定义 --end 的算法时,是否需要?如果是这样,结果应该是可取消引用的吗?

void algo(T beg, T end){
    //...
    auto iter = --end;
    //...
    *iter;
} 

【问题讨论】:

    标签: c++ stl iterator


    【解决方案1】:

    如果算法需要由双向迭代器firstlast 定义的范围,那么--last 需要在与++first 相同的条件下有效——即范围不为空。该范围为空当且仅当first == last

    如果范围不为空,则--last 会计算为引用范围中最后一个元素的迭代器,因此*--last 确实也需要有效。

    也就是说,没有那么多标准算法需要专门的双向迭代器(并且不需要随机访问)。 prevcopy_backwardmove_backwardreversereverse_copystable_partitioninplace_merge[prev|next]_permutation

    如果您查看其中一些函数的作用,您应该会发现该算法通常会减少范围结束迭代器并取消引用结果。

    正如 James 所说,对于容器,函数 end() 返回一个迭代器按值。当x 是该类型的右值时,对于迭代器,没有一般要求--x 应该是一个格式良好的表达式。例如,指针是双向迭代器,声明为int *foo(); 的函数按值返回指针,而--foo() 不是格式良好的表达式。碰巧的是,对于您在实现中查看的容器,end() 返回一个将operator-- 定义为成员函数的类类型,因此代码编译。由于容器不是空的,它也可以工作。

    请注意,在这方面有以下区别:

    auto last = --c.end();
    

    对比

    auto last = c.end();
    --last;
    

    前者递减一个右值,而后者递减一个左值。

    【讨论】:

      【解决方案2】:

      你读错了。表达式--c.end() 从未被授权。如果 迭代器至少不是双向的,事实上,它是明确的 禁止,并且需要编译器错误。如果集合为空, 这是未定义的行为。在所有其他情况下,它会工作如果 它可以编译,但不能保证它会编译。它失败了 使用std::vector 的许多早期实现进行编译,对于 例如,迭代器只是指向指针的 typedef。 (实际上, 我正式认为在所有情况下它都是未定义的行为,因为 您违反了对模板化实现的约束。在 练习,但是,你会得到我刚才描述的。)

      可以说,因为不能保证,一个好的实现会导致 它无法系统地编译。由于各种原因,大多数人不这样做。 不要问我为什么,因为让它失败非常简单 系统地:只需将迭代器上的operator-- 设为免费 函数,而不是成员。

      编辑(附加信息):

      它不是必需的事实可能是很大一部分 C++11 中 std::nextstd::prev 背后的动机。当然, 无论如何,我从事的每个项目都有它们。正确的方法 写成这样:

      prev( c.end() );
      

      当然,迭代器是双向的或 更好,而且容器不是空的,还是拿着。

      【讨论】:

        【解决方案3】:

        每个算法都会告诉你它需要什么类型的迭代器。当调用双向迭代器时,自然需要支持递减。

        --end是否可能取决于end == beg是否。

        【讨论】:

        • @SteveJessop:我不同意编辑。 beg 是什么无关紧要,只要endbegin() 不同即可。
        • 好的,如果你恢复它,那么我不会编辑战争。但是,没有标准算法会将迭代器回绕到它被指示操作的范围的起点之前,因此尽管--end 可能对某些空输入范围有效,但算法不会执行它,所以它不是必需有效。此外,“向算法提交一对双向迭代器时”,并没有要求迭代器来自容器,所以我认为begin()不应该进入它。您的版本是正确的,但(IMO)与问题无关。
        • @SteveJessop:尽管迭代器必须具有可比性——这不意味着它们必须来自同一个容器吗?
        • 不,例如它们可以是指向数组的指针,或者它们可以是独立于任何容器运行的用户定义的双向迭代器。在我的脑海中,我认为所有双向的标准迭代器都与容器有关。但我可能忘记了什么。
        • @SteveJessop:如果它们是指向数组的指针,那么它们肯定必须来自同一个数组,或者它是 UB。但我知道如果没有容器,你怎么能有更一般的情况......
        【解决方案4】:

        只有需要双向迭代器的算法才需要。

        【讨论】:

        • 那么就不需要了。我使用了无法编译的std::vector 的实现。
        猜你喜欢
        • 2011-07-16
        • 2021-04-10
        • 2021-11-25
        • 2021-11-21
        • 2021-01-27
        • 1970-01-01
        • 2011-09-20
        • 2011-07-04
        • 1970-01-01
        相关资源
        最近更新 更多