STL iterator before std::map::begin()

前端 未结 3 1994
暖寄归人
暖寄归人 2021-01-12 01:33

In C++11\'s std::map, is there some valid iterator x such that ++x is guaranteed to equal map::begin()? I would like to detect i

相关标签:
3条回答
  • 2021-01-12 02:17

    No, iterators before the beginning in std containers are all UB (except for reverse iterators, which will probably not solve your problem).

    You probably need to fix the function in question. Failing that, wrap it and catch the bad behavior before you call it. Failing that, you could insert a negative infinity element into the map key type ordering, and add a sentinal value. Failing that, you could write iterator adapters that wrap your map iterators with ones that can go one-before-beginning without UB.

    These are ordered in my order of recommendation, roughly. Each has ways it could fail, and they get more error prone and dangerous as my recommendation gets more remote.

    0 讨论(0)
  • 2021-01-12 02:22

    It's very important to realize that Standard Library containers are semi-open ranges [begin, end), i.e. you can iterate one-past-the-end. For bidirectional (and random) iterators you can also do --end() and come back from the brink. Dereferencing one-past-the-end by *end() is undefined behavior, and so is decrementing the begin iterator by --begin() or begin() - 1. There is only one exception to this: std::forward_list which has a non-dereferenceable iterator before_begin() that satisfies ++before_begin() == begin() (but note that for a forward_list you cannot decrement begin() either).

    This fundamental asymmetry for bidirectional iterators means that reverse iterators are thin wrappers around regular iterators. In most Standard Library implementations they simply contain a copy base_ of the underyling iterator. Incrementing a std::reverse_iterator calls something like --base_; return *this;, and dereferencing it does auto old = base_; return *--old;. At no point is the underlying iterator decremented to before begin(), and no dereferencing of end() is done that way.

    Below are the four ways to iterate over a container supporting bidirectional or random iterators, and the relations between the various iterators (.base() converts a std::reverse_iterator back to its underlying 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);
            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";
        }
    }
    

    Live Example

    If you have data structure that should support bidirectional iteration but there are no member iterators .rbegin() or rend(), you can easily define them yourself by std::reverse_iterator(end()) and std::reverse_iterator(begin()), respectively (this is also the way the Standard Library usually implements them).

    0 讨论(0)
  • 2021-01-12 02:24

    By "walk the iterator off the front" I presume you are decrementing a forward iterator something like this:

    // don't do this:
    for(it = mymap.end(); --it >= mymap.begin(); ) { ... }
    

    Instead, increment a reverse iterator like this:

    // this is better:
    for(it = mymap.rbegin(); it != mymap.rend(); ++it) { ... }
    

    -Jesse

    0 讨论(0)
提交回复
热议问题