2

If I understand the answer of "Why is comparing against "end()" iterator legal?" correctly, I can compare an end() iterator against a singular iterator. So we do it like here:

template <typename Iterator>
static bool isSingular(Iterator i) { return i == Iterator(); }

However, if this is called with an end() iterator I do get the following debug error (in a debug build -> map/set iterators incompatible). Is that error wrong, or is the code wrong and the error justified?


Win64 debug build error:

bool operator==(const _Tree_const_iterator& _Right) const
        {   // test for iterator equality
 #if _ITERATOR_DEBUG_LEVEL == 2
        if (this->_Getcont() != _Right._Getcont())
            {   // report error
            _DEBUG_ERROR("map/set iterators incompatible");
            }

Debugger

Horst Walter
  • 13,663
  • 32
  • 126
  • 228
  • Please post a MCVE. It's not clear from your question what you are comparing exactly – M.M Mar 01 '18 at 22:08
  • correct, but it should not match the default constructed iterator (since C++14) and a should be a correct comparison not causing an error – Horst Walter Mar 01 '18 at 22:10
  • First, you read [MS's blurb on iterator debug levels](https://msdn.microsoft.com/en-us/library/hh697468.aspx) while researching this, yes? The error you're getting is a runtime error, not compile time, in case anyone else missed that part.. The MS debug-iterator nuance is effectively telling you the code may be unsafe. – WhozCraig Mar 01 '18 at 22:13
  • Even if a default constructed iterator is singular, it doesn't mean that all singular iterators are default constructed. Or comparable. – Bo Persson Mar 01 '18 at 22:13
  • _If_ it were singular, https://stackoverflow.com/q/17198239/560648 would be relevant. – Lightness Races in Orbit Mar 01 '18 at 22:17
  • You are assuming that `std::map`'s `end()` iterator is singular or can be compared to a singular iterator. In fact, `Iterator()` is not guaranteed to compile at all, it is not a requirement of iterators, it doesn't have to have a singular representation. – kmdreko Mar 01 '18 at 22:17
  • @Robinson: Quite the opposite: it'll _usually_ be some kind of sentinel value. Only with a contiguous (or partially contiguous) container can you have a physical "one past the end" location. Okay I suppose even a map or list could allocate a dummy end node, but ... they won't. – Lightness Races in Orbit Mar 01 '18 at 22:18

2 Answers2

4

If I understand the answer of "Why is comparing against "end()" iterator legal?" correctly, I can compare an end() iterator against a singular iterator.

That is incorrect understanding.

The iterator returned by std::vector::end() is not a singular iterator. It is associated with an instance of std::vector.

The standard states that:

Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation.

It does not specify explicitly whether comparing two singular iterators or a singular iterator with a non-singular iterator is incorrect or undefined behavior.

It obviously is not a problem for simple pointers but the standard leaves it open for compiler implementations to deal with comparing singular iterators with other iterators, singular or not, corresponding to the standard library containers.

In your case, the compiler does not allow it.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 1
    I don't think that quote claims that `end()` is a singular iterator. It says you can compare `end()` to a singular iterator, and [it's wrong](https://stackoverflow.com/q/17198239/560648) (in general). – Lightness Races in Orbit Mar 01 '18 at 22:19
  • 1
    @LightnessRacesinOrbit, are you implying that you can't compare two singular iterators? – R Sahu Mar 01 '18 at 22:23
  • I'm not just implying it, I'm stating it outright. See https://stackoverflow.com/q/17198239/560648 – Lightness Races in Orbit Mar 01 '18 at 22:24
  • "It obviously is not a problem for simple pointers" - in the context of pointers, uninitialized pointers are singular iterators, and they cannot be compared with anything. So I think that should say the total opposite: "It obviously is the same problem for simple pointers". – MSalters Mar 02 '18 at 10:37
  • @MSalters: Did you mean to post that comment under Casey's answer instead? – Lightness Races in Orbit Mar 02 '18 at 10:45
  • @LightnessRacesinOrbit: No, The quoted "It obviously is not a problem for simple pointers" is from the penultimate sentence of this (R Sahu's) answer, and the problem it refers to is in the sentence preceding it. Also, "Casey's answer?" – MSalters Mar 02 '18 at 11:11
  • @MSalters: Apologies missed that line. Casey's answer is the one on the page I linked to immediately before your comment, which claims the same thing. – Lightness Races in Orbit Mar 02 '18 at 11:19
  • @MSalters, while it's true that you cannot compare uninitialized pointers, you can compare any pointer with `nullptr`. – R Sahu Mar 02 '18 at 15:28
3

No answer on that other question claims that end() returns a singular iterator (it doesn't).

Regardless, in general, you can't compare anything to a singular iterator, not even another singular iterator! (though it may sometimes happen to appear to work).

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 2
    You can compare value-initialized forward-or-better iterators with each other since C++14. But you still can't compare them with other iterators. – T.C. Mar 01 '18 at 22:27
  • @T.C. hasn't it always been a common idiom to compare with singular value for stream iterators, e.g. `copy(istream_iterator(cin), istream_iterator(), back_inserter(vec));` will compare the non-singular first iterator with the singular second argument , at each iteration – M.M Mar 01 '18 at 23:43
  • 1
    @M.M Who said a default-constructed `istream_iterator` is singular? – T.C. Mar 02 '18 at 00:25
  • @T.C. the standard says "Iterators can also have singular values that are not associated with any sequence". The standard doesn't define "singular" in any other way, so I am taking this as a definition, i.e. an iterator is singular iff it is not associated with any sequence. [litb concurs with this](https://stackoverflow.com/a/5441944/1505939). `istream_iterator()` is not associated with any particular sequence, therefore it is singular by this definition. – M.M Mar 02 '18 at 00:48
  • @M.M It's more properly considered the past-the-end value of every sequence - i.e., associated with *every* sequence rather than none of them. – T.C. Mar 02 '18 at 00:50
  • @T.C. Does it say somewhere in the standard: for which containers is it true that a default-constructed iterator is a past-the-end value for every sequence? (Seeing as OP's `Iterator()` presumably is not, whereas `istream_iterator()` is). – M.M Mar 02 '18 at 00:55
  • Also it seems to me that OP was meaning to ask about `i == Iterator()` specifically, perhaps considering that default-constructed iterator was the definition of "singular iterator" and thus writing a misleading title; and further, an answer should explain why `Iterator()` is or isn't singular – M.M Mar 02 '18 at 00:55
  • @M.M: _stream_ iterators are a subset of iterators, and _singular stream_ iterators are a subset of both _stream_ iterators and _singular_ iterators. Special rules specifically for them do not generalize. – MSalters Mar 02 '18 at 10:39