0

According to the [iterator.concepts.general]:

Otherwise, if iterator_traits names a specialization generated from the primary template, then ITER_CONCEPT(I) denotes random_access_iterator_tag.

ITER_CONCEPT(I) may be std::random_access_iterator_tag, even if I models input_iterator.
What is the purpose of this? Why does c++ 20 iterator concept(such as std::forward_iterator) still checks whether the ITER_CONCEPT is derived from a certain tag? Example:

template<class I>
concept forward_iterator =
input_iterator<I> &&
derived_from<ITER_CONCEPT(I), forward_iterator_tag> && //is this necessary?
incrementable<I> &&
sentinel_for<I, I>;
康桓瑋
  • 33,481
  • 5
  • 40
  • 90
a.tana
  • 41
  • 7
  • 1
    "According to the rule..." What rule? From where did you get that "rule"? And please [edit] your question to fix possible spelling errors (what is "befog"?) – Some programmer dude Jan 20 '22 at 07:37
  • Related: [Interpreting C++20 standard description of `ITER_TRAITS`](https://stackoverflow.com/questions/70192223/interpreting-c20-standard-description-of-iter-traits). – Evg Jan 20 '22 at 07:47
  • @Someprogrammerdude, I got the rule from [here(page 914, section 23.3.4)](https://isocpp.org/files/papers/N4860.pdf). – a.tana Jan 20 '22 at 07:51
  • 1
    @Someprogrammerdude, I'm sorry for my poor English, but "befog" seems to be a legit word.[link](https://www.oxfordlearnersdictionaries.com/definition/english/befog). Or do you suggest better wording? – a.tana Jan 20 '22 at 08:04
  • "*Why does c++ 20 iterator concept(such as std::forward_iterator) still checks whether the ITER_CONCEPT is derived from a certain tag?*" Because `I` may define the member type of `iterator_concept`, I'm not quite sure what you are trying to ask. – 康桓瑋 Jan 20 '22 at 08:12
  • @康桓瑋, If I defines the member type of `iterator_concept`, as `std::forward_iterator_tag`, the checking `derived_from` will succeed, but even if I does not define the member, the checking may still succeed, because of the "rule" I referenced(ITER_CONCEPT(I) may denote `std::random_access_iterator_tag`). – a.tana Jan 20 '22 at 08:37
  • @a.tana Although the check will succeed, whether `I` satisfies the `forward_iterator` still needs to check the operations supported by `I`. And If the `I`'s `iterator_concept` is defined as `input_iterator_tag`, then `derived_from` will fail early. – 康桓瑋 Jan 20 '22 at 09:03
  • @康桓瑋, so, ITER_CONCEPT is just a mechanism to (possibly)fail early? – a.tana Jan 20 '22 at 09:23
  • @a.tana *ITER_CONCEPT* returns `iterator_concept`/`category` which (if present) indicates the category of that iterator. `forward_iterator` concept needs to use `ITER_CONCEPT` to try to get the category of that iterator. But what I don't understand is why *ITER_CONCEPT* chooses to use `random_access_iterator_tag` instead of `contiguous_iterator` in such case. – 康桓瑋 Jan 20 '22 at 13:36
  • @康桓瑋, It's another mystery that it chooses `std::random_access_iterator_tag` instead of `std::contiguous_iterator_tag`. Because of that, `std::contiguous_iterator` concept is the only one iterator concept which requires the iterator to have member type `iterator_concept`(or `iterator_category`) and it needs to be `std::contiguous_iterator_tag`. – a.tana Jan 20 '22 at 14:41
  • @a.tana: Contiguous iterator (at one point) did not have any syntactic differences between itself and random access iterator. Therefore, the *only* way to tell whether an iterator *intends* to support contiguity is for it to explicitly declare itself such. Granted, even after syntactic differences were added, it's still a good idea not to assume an iterator is contiguous, since that can be a very dangerous assumption. – Nicol Bolas Jan 20 '22 at 14:52
  • @NicolBolas, That's understandable some what. But then, why ITER_CONCEPT needs to be checked for other iterator concepts? Checking only syntactic requirements seems enough for non-contiguous_iterators? – a.tana Jan 20 '22 at 15:03
  • @a.tana: Because if your type declares that it is only an X iterator, then the standard should believe it, even if it could be a Y. – Nicol Bolas Jan 20 '22 at 15:05
  • @NicolBolas, thanks. but implementer can also mistakenly set weaker iterator category tag. Is that one of the reason iterator concepts check syntactical requirements? – a.tana Jan 20 '22 at 15:26
  • @a.tana: "*but implementer can also mistakenly set weaker iterator category tag.*" And they will find out their mistake when they try to pass it to a function that requires a stronger iterator type. Mistakes that result in compile errors are better than mistakes that result in *maybe* runtime errors. – Nicol Bolas Jan 20 '22 at 15:32
  • @NicolBolas, They will also be suffered from unknown runtime errors, because ITER_CONCEPT let weaker iterators(or even non iterators) pass stronger iterator concepts just because they satisfy syntactical requirements. – a.tana Jan 20 '22 at 15:55
  • @a.tana: If the user did not provide an iterator tag, then the user has clearly said that it's OK to just use its syntax. That is, the user has chosen to open themselves up to these errors. Nothing can prevent *all* runtime errors, , but the current system prevents more than any of the alternatives. – Nicol Bolas Jan 20 '22 at 16:12
  • @NicolBolas, not providing an iterator tag can easily be a sign of a negligence. "but the current system prevents more than any of the alternatives" this is clearly wrong. ITER_CONCEPT is evaluated as the second strongest iterator category tag. this is, unquestionably not for preventing errors. completely opposite. – a.tana Jan 20 '22 at 16:40
  • @a.tana: "*ITER_CONCEPT is evaluated as the second strongest iterator category tag. this is, unquestionably not for preventing errors.*" I'm not sure I understand your meaning here. The point is that, ***if you don't specify an iterator tag***, the system will deduce the kind of iterator purely from syntax. The only other alternative is to force a user to always provide a tag. – Nicol Bolas Jan 20 '22 at 16:47
  • @NicolBolas, "The only other alternative is to force a user to always provide a tag" or ITER_CONCEPT can be a weaker iterator category tag such as `input_iterator_tag`, if you don't specify an iterator tag. – a.tana Jan 20 '22 at 17:01
  • @a.tana: That has all of the problems of forcing the user to provide a tag (ie: getting lots of compile errors for code that ought to work) and none of the benefits (ie: a clean, clear error that tells them exactly what they need to do to fix it). – Nicol Bolas Jan 20 '22 at 17:12
  • @NicolBolas, I'm not saying that's good, though that prevents some runtime errors("compile errors are better than mistakes that result in maybe runtime errors"). I just feel there is inconsistency. Maybe I should understand that the behavior of ITER_CONCEPT is a valid compromise. so thanks Nicol. – a.tana Jan 20 '22 at 17:41

1 Answers1

2

To be an X iterator, the iterator concept requires (among other things) two things. It is possible for a type to accidentally fulfill an iterator's syntactic requirements without intending to. As such, to be an X iterator, the prospective iterator type must declare that it intends to be (at least) an X iterator. This is done through the tag type, as retrieved through ITER_CONCEPT.

But iterators have an "inheritance" graph of sorts. All random access iterators are also bidirectional. So each iterator concept requires that the prospective iterator type fulfill the concept requirements of its "base" iterator concept.

These are separate requirements. Types can lie, after all, and the whole point of concepts is to keep types from lying. Just because a type claims to be a forward iterator does not mean it actually is one.

As for the purpose of this particular rule in ITER_CONCEPT, it is to bypass the defaulting mechanism defined for the legacy iterator_category. The idea is that if you haven't specified your iterator's tag type explicitly, then ITER_CONCEPT will assume the (second) most permissive one and then use C++20's concept checks to see what your iterator actually supports.

It doesn't mean that this iterator is definitely a random access iterator; it's saying "try (almost) anything, and if the concept doesn't fit, then that isn't it".

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • This is a very comprehensive explanation, +1. Although it feels weird to me that for an arbitrary type `A`, `ITER_CONCEPT(A)` would be `random_access_iterator_tag`. – 康桓瑋 Jan 20 '22 at 15:11
  • @康桓瑋: It's not for "an arbitrary type". It's only for a type that *did not* otherwise specify its iterator tag. Which is rather rare. – Nicol Bolas Jan 20 '22 at 15:13
  • The standard doesn't constrain `ITER_CONCEPT` to only be applied to "specific" types, which make `ITER_CONCEPT` a `random_access_iterator_tag`, which doesn't make sense... – 康桓瑋 Jan 20 '22 at 15:24
  • 2
    @康桓瑋: What does it matter? `double` will fail the *other* requirements of any of the iterator concepts. That's why `ITER_CONCEPT` is an implementation detail, not an exposed API. It's a tool that gets used in a specific context. – Nicol Bolas Jan 20 '22 at 15:33
  • @NicolBolas, I feel that your explanation introduce a contradiction. You wrote "the prospective iterator type must declare that it intends to be (at least) an X iterator." then why ITER_CONCEPT pretends as if the prospective iterator has `std::random_access_iterator_tag` even if the iterator does not. – a.tana Jan 20 '22 at 15:40