std :: map :: begin()之前的STL迭代器

Geo*_*ard 7 c++ iterator stl c++11 reverse-iterator

在C++ 11中std::map,是否有一些有效的迭代器x使得++ x保证相等map::begin()?我想检测一下我刚刚调用的函数(我的)是否已经从函数前面走了一个迭代器.该函数将迭代器向后移动一个位置.

答案是否适用于图书馆的其他部分?

Tem*_*Rex 7

意识到标准库容器是半开放范围非常重要[begin, end),即您可以迭代最后一个。对于双向(和随机)迭代器,您还可以执行--end()并从边缘返回。取消引用 one-past-the-end by*end()是未定义的行为,通过--begin()or递减开始迭代器也是如此begin() - 1。只有一个例外:std::forward_list它有一个不可解引用的迭代器before_begin()满足++before_begin() == begin()(但请注意,对于 aforward_list你也不能递减begin())。

双向迭代器的这种基本不对称性意味着反向迭代器是常规迭代器的薄包装器。在大多数标准库实现中,它们只包含base_底层迭代器的副本。增加std::reverse_iterator调用类似的东西--base_; return *this;,并取消引用它auto old = base_; return *--old;。底层迭代器绝不会减少到 before begin(),并且不会以end()这种方式取消引用。

以下是迭代支持双向或随机迭代器的容器的四种方法,以及各种迭代器之间的关系(.base()将 a 转换std::reverse_iterator回其底层迭代器)

#include <iomanip>
#include <iostream>
#include <iterator>
#include <map>
#include <string>

int main()
{    
    auto c = std::map<int, std::string>{ {1, "hello"}, {2, "world"} };
    
    {   // 1) forward iteratation
        auto it = begin(c);
        for (; it != end(c); ++it){}
        std::cout << std::boolalpha << (it == c.rbegin().base()) << "\n";
    }

    {   // 2) meh, backward iteration
        auto it = end(c) - 1; //end return iterator after the last element.
        for (; it != begin(c); --it){}
        std::cout << std::boolalpha << (it == c.rend().base()) << "\n";
    }

    {   // 2') better: reverse iteration
        auto it = c.rbegin();
        for (; it != c.rend(); ++it){}
        std::cout << std::boolalpha << (it.base() == begin(c)) << "\n";
    }

    {   // 1') backward reverse, better avoid this
        auto it = c.rend();
        for (; it != c.rbegin(); --it){}
        std::cout << std::boolalpha << (it.base() == end(c)) << "\n";
    }
}
Run Code Online (Sandbox Code Playgroud)

现场示例

如果您的数据结构应该支持双向迭代,但没有成员迭代器.rbegin()or rend(),您可以分别通过std::reverse_iterator(end())和轻松定义它们std::reverse_iterator(begin())(这也是标准库通常实现它们的方式)。

  • @aaronman 很遗憾听到您对否决票感到不安。公平地说,我是 3 个投票者中唯一一个解释我这样做的原因的人。请不要把它个人化,我没有说你的回答是废话,但 SO 的答案也应该对未来的读者有用。UB 真的是魔鬼,因为它可以*无声地* 破坏您的代码。 (4认同)

Yak*_*ont 5

不,std容器中开始的迭代器都是UB(反向迭代器除外,这可能无法解决您的问题)。

您可能需要修复有问题的功能。如果失败,则将其包装起来并在调用之前捕获不良行为。如果失败,则可以将负无穷大元素插入map键类型排序中,并添加一个小数位值。失败的话,您可以编写迭代器适配器,将map迭代器与可以在没有UB的情况下开始使用的迭代器包装在一起。

这些按我的推荐顺序大致排序。每种方法都有失败的可能,而且随着我的建议越来越遥远,它们会更容易出错和危险。

  • 迭代器包装器乍一看“似乎”很干净,然后我想到我将如何使用它们,它很快就变得非常令人讨厌。 (2认同)
  • 哎呀,“ std :: forward_list”确实有一个“ before_begin()”成员 (2认同)