13

I understand that the standard allows std::vector<int, A> to have the same type of iterators for different allocators A. This is called SCARY iterators.

Now the question is does the standard allow std::vector<int, A>::iterator be simply a typedef of A::pointer, thus making it just an int* for the default allocator?

Or is there some (implicit) requirement for it to be a separate class type per container? If there is no such requirement then why all major implementations (including the SCARY ones) don't use this approach? It would presumably reduce compiler work even further, though now code that overloads on int* and vector<>::iterator will not compile.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • 11
    perhaps some don't know about [SCARY iterators](http://stackoverflow.com/questions/14391705/what-are-scary-iterators) and are voting assuming they don't actually exist? That's the only thing I can think of... – jaggedSpire Jul 19 '16 at 21:19
  • 2
    What makes it "super" SCARY? – zneak Jul 19 '16 at 21:19
  • 2
    @zneak: "typical" SCARY iterators mean that they are still a traditional class that is simply defined outside the templated class. By "super" I mean that this is one level SCARYer than those :) Essentially it means that `std::vector`, `std::array` and `std::initializer_list` could all share the same iterator type... – Yakov Galka Jul 19 '16 at 21:21
  • 20
    I just looked it up. SCARY is an acronym that means: **S**eemingly erroneous (**C**onstrained by conflicting generic parameters), but **A**ctually work with the **R**ight implementation (unconstrained b**Y** the conflict due to minimized dependencies). That's one of the worst forced acronyms I've ever seen. – Taywee Jul 19 '16 at 22:50
  • 1
    @Taywee That is positively ridiculous. And I though the GADZOOKS (Gadolinium Antineutrino Detector Zealously Outperforming Old Kamiokande, Super!) project was bad. – ApproachingDarknessFish Jul 20 '16 at 00:47
  • Self-referential acronyms are maybe in a class of their own, like GNU = **G**NU is **N**ot **U**nix. One could equally well call it ANU, **A**NU is **N**ot **U**nix. Except for the logo, of course. On the other hand, calling it SNAFU, for **SNA**fu **A**int **F**ickling **U**nix, wouldn't have the same ring to it. TIPOT, This Is Pretty Off Topic. – Cheers and hth. - Alf Jul 20 '16 at 02:08

2 Answers2

7

Re

does the standard allow std::vector<int, A>::iterator be simply a typedef of A::pointer

As far as I know, yes. But not std::vector<bool, A>, because that's a specialization where a dereferenced iterator is a proxy object that accesses whatever representation is used, with the intent of supporting one bit per bool.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
5

Now the question is does the standard allow std::vector<int, A>::iterator be simply a typedef of A::pointer, thus making it just an int* for the default allocator?

Not on just any implementation, I think.

24.2 Iterator requirements [iterator.requirements]

24.2.1 In general [iterator.requirements.general]

11 In the following sections, a and b denote values of type X or const X, [...]

24.2.7 Random access iterators [random.access.iterators]

Expression | Return type    | Operational | Assertion/note
           |                | semantics   | pre-/post-condition
-----------+----------------+-------------+--------------------------------
[...]
-----------+----------------+-------------+--------------------------------
a < b      | contextually   | b - a > 0   | < is a total ordering relation
           | convertible to |             |
           | bool           |             |

Note that unlike the earlier requirement on -, there is no precondition for < that a and b are iterators of the same container. < is required to form a total ordering relation for arbitrary iterators. < is not required to form a total ordering relation for arbitrary pointers. While implementations are permitted to extend the definition of < for raw pointer types to allow comparing unrelated pointer values, popular current real-world implementations do not do so, because such an extension would prevent some optimisation opportunities.

Community
  • 1
  • 1
  • Hmm, but pointers are meant to satisfy the same requirements of random access iterators, always. Aren't they? Perhaps the "total ordering" requirement is a bug in the standard? – Yakov Galka Jul 19 '16 at 22:33
  • 3
    I believe that the "total ordering" requirement is a bug in the standard – Mooing Duck Jul 19 '16 at 22:33
  • On the one hand, if this weren't required, there at least should be a requirement for an additional requirement on `std::less` that it forms a total ordering for iterators as well as pointers, but that's only there for pointers. On the other hand, the text does strongly suggest that it's possible for a pointer type to implement the requirements for random access iterators. So I don't believe it's a bug. I don't believe it's not a bug. I simply don't know what the intent is. I can only draw conclusions on what's been written, not what's been thought. –  Jul 19 '16 at 22:43
  • 5
    Since `a < b` is defined as `b - a > 0`, the constraints for `-` also apply to `<`. – Pete Becker Jul 19 '16 at 22:49
  • @PeteBecker The standard in general doesn't work like that, and `std::less` for pointer types is a good example of that as its `operator()` is simultaneously defined as returning `x < y` and as forming a total ordering relation. That's most certainly intended to mean that only when `x < y` is defined, `std::less<>()(x, y)` is required to give the same result, but `std::less<>()(x, y)` is still allowed even when `x < y` isn't. But if you're right: that would mean we can't have a `set` of iterators, as there is nothing that guarantees a useful relational comparison exists. –  Jul 19 '16 at 22:59
  • 2
    "...popular current real-world implementations do not do so..."—I'm quite sure they do. 1) AFAIK for all linear architectures pointer `<` gives total order. 2) libc++ implements std::less by simply calling `<`, so it already relies on pointers being totally ordered, so it could use them for `vector::iterator`? Interestingly it does define `std::array` iterators as pointers though. – Yakov Galka Jul 19 '16 at 23:14
  • @hvd You can have a set of random access iterators, just like you can have a set of doubles. They just have to be all into the same container - or for doubles, not NaN. – T.C. Jul 19 '16 at 23:44
  • The `RandomAccessIterator` constraints don't imply that `a` and `b` are iterators to the same container (or that there is even a container involved!). However, it does imply that every iterator in the range `[min(a,b), max(a,b))` is dereferenceable. –  Jul 20 '16 at 03:40
  • @ybungalobill "1) AFAIK for all linear architectures pointer < gives total order." -- Even if that's what the hardware could do, compilers can exploit the fact that `<` is undefined for pointers into unrelated arrays for optimisation purposes: the mere fact that a comparison `a < b` is being performed tells the compiler it can assume that `a` and `b` are pointers into the same array. "2) libc++ implements std::less by simply calling `<`" libc++ is meant to be used with clang, and if clang decides to extend `<` to guarantee a total ordering relation, then sure, libc++ can make use of that. –  Jul 20 '16 at 04:53
  • @T.C. Heh, I suppose that's another way around it. Requiring them to be of the same container is a serious restriction though, and it's a restriction that's implied not to exist by the claim that `<` gives a total ordering relation. –  Jul 20 '16 at 04:55
  • @Hurkyl `a` and `b` are any valid value of the iterator type, without restriction even that a range `[min(a,b), max(a,b))` exists. –  Jul 20 '16 at 04:59
  • @ybungalobill BTW, libstdc++ also appears to implement `std::less` as just using `<` for all types, I don't see a partial specialisation for pointer types, and that makes me think. I was convinced it was GCC that exploited the UB, and now I'm not sure if 1) I'm missing the partial specialisation, 2) GCC only exploits the UB in specific scenarios and a helper function is enough to bypass it 3) GCC changed 4) libstdc++ has a bug, or 5) I'm remembering wrong. –  Jul 20 '16 at 05:04
  • Whoops, my previous comment was meant to start with "The `RandomAccessIterator` constraints for `-` don't imply...". –  Jul 20 '16 at 05:05
  • @Hurkyl Ah, fair enough. I haven't really paid attention to what the exact requirements are for `-` and how that applies to iterators in general, but specifically for pointers, what you say is equivalent to requiring them to be pointers into or one past the same array anyway, is it not? –  Jul 20 '16 at 05:13
  • 1
    @hvd: Obviously the compiler *could* do such an optimization. But my line of thought was that given that `stdc++` already assumes total order of pointers, then it could go one step further and typedef `std::vector<>::iterator` to a pointer. But it `wraps<>` it instead, which raises the question 'why'? – Yakov Galka Jul 20 '16 at 08:26
  • 1
    @Hurkyl: it does not imply. Assume `int x[10],y[10],*a=x,*b=y;`, then `a` and `b` are presumably `RandomAccessIterators` but not everything in the range `[min(a,b), max(a,b))` is dereferencable. In fact the expression `min(a,b) + 11` is already undefined. – Yakov Galka Jul 20 '16 at 08:29
  • @ybungalobill: follow through how `-` depends on `+` and then `++`. It's the last of these for `InputIterator`s that ultimately asserts dereferenceability. –  Jul 20 '16 at 09:04
  • @Hurkyl: But your claim directly contradicts the concrete example I gave you. So it's either a bug in the standard (less likely) or we don't understand something (more likely). – Yakov Galka Jul 20 '16 at 11:09