-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Raw pointer does not satisfy the requirements of RandomAccessIterator #154
Comments
Somewhere it needs to say that comparison ops for iterators are only required to be well-defined given iterators into the same range. So, in the domain of |
That. |
This should apply not only for algorithms, but also to comparison operators of iterators wrappers like |
The issue isn't specific to
Which are collectively intended to imply that it's the users responsibility to ensure that all the values of objects a library function is specified to access through its arguments are in a valid domain of all expressions required of their types by the associated constraints. So if you e.g. pass a range of I'm going to spend some time expanding and/or clarifying that wording in the next few days, so I'd appreciate any feedback. |
So, if I am understanding this correctly, we support types for which Do we allow |
Yes, that's exactly correct; the semantics are all about observable behavior. If the expressions required by
|
This is important question regarding to requirement placed on |
As it's specified now, |
Actually none of random access iterators defined in STL (raw pointer, We already agreed that for algorithms like For use cases, we can imagine structure for which the comparison of iterators to different containers will return |
Sorry, for mistake. |
I now understand the problem description, and I think it's an interesting problem to solve. Actually I see two problems here:
The second problem is in the purview of the Ranges TS in that we need to determine how to solve that problem for iterators and algorithms, but the first is out of scope. I think it would be waste of effort to attempt to address the second problem until the ongoing research in the committee settles on a solution for the first. |
@CaseyCarter Can you summarize the problem as you see it? I don't see an issue here. See also issue #21, std::less and friends may be (are probably) over-constrained. |
Equational reasoning doesn't need I had an epiphany that If you want a strict weak ordering that defaults to template<class T, StrictWeakOrder<T> C = less<T>>
void f(); if you want template <StrictTotallyOrdered T>
void g() {
auto cmp = less<T>{};
// Do stuff with cmp, which we know induces a strict total order because it cannot weaken
// the guarantees provided by T.
} Not that the definition of Overconstraining the standard comparison function objects doesn't help writers of code that want stronger constraints, it only excludes writers of code that want weaker constraints. I had planned to write a paper discussing this and proposing the proper semantic constraints for the default implementations of the comparison function objects and for user-defined specializations thereof, but the idea was crushed on the rocks of |
For now, I think perhaps the best we can do is to relax the requirements on the primary templates and forbid user specialization of EDIT: "forbid user specialization" may be best enforced, as with |
Someone needs to review this discussion thoroughly, and summarize the current status of the issue so we can properly assess if there's something here that still needs fixing. |
@tomaszkam wrote:
This seems to presuppose something that doesn't match my understanding: that only singular values can fall outside the domain of an operation. Zero is a well-formed value of type We can carefully define the domain of required expressions, and for some operations we do (e.g. [iterators.sentinel]/p3). The operations are not required to be complete for the type to satisfy the concept. We already say that. I am still of the opinion that pointers are totally ordered in the domain of |
But if we move forward this path, then we can take any We are following exactly the same reasoning for the But all of above does not change the fact that |
Let define But if you want to create an subset
|
So the number of values that are outside the domain matters when deciding whether a type satisfied a concept? Not sure I buy that. There are by my count 2^23-1 different values of NaN in IEEE 754. Is that too many for
We are saying that pointers that are outside the domain are not ordered.
When users pass a range of pointers to |
There is no problem if we agree on that interpretation - we only care if the concept requirement are imposed on the values passed to the specific invocation, and we do not care about all other possible values of the objects of given type. For example, is the following interpretation correct?
If only following two invocations are executed in program, the program is valid regardless of validity/result of the calls of |
Just like the IS, we say that But meanwhile |
No we don't. We only make special accommodation for
No we don't. |
My HTML generated from current trunk says - and so does the LaTeX source:
Is there a pending issue resolution that I missed? |
Cripes. Is my memory glitching? Apologies for the FUD. :-(
Now wait a minute. I know we fixed this in 0f70344 as part of the discussion of #151. What happened to that change??? @CaseyCarter? EDIT: Looks like @CaseyCarter reverted that fix in 8914792. I'm not sure why. |
LEWG rejected the changes to the comparison function objects from P0370R0. The feeling was that it was too permissive to allow users to specialize the comparisons to have meaning different from the operators. My personal opinion is that the comparisons should impose no requirements on the parameters: they should be exactly and only names for the operators, propagating the requirements and properties of those operators. The comparisons are virtually unimplementable in the IS right now. To impose a total ordering on pointers the comparisons must use representations, but to be usable in constant expressions they cannot access representations. Also, there's the problem that representations of pointers may not be fixed until load time. Queue the laugh track. None of the above says anything about making the comparisons work with pointers in the TS. I agree that it's seriously murky what it means for e.g. requires is_pointer<T>::value && is_pointer<U>::value || (/*...pre-existing requirements...*/) |
We could quickly extract the wording for just this change from 0f70344. (EDIT: instead of the obviously broken off-the-cuff suggestion I make above.) |
As to the rest of the discussion: We've been trying to avoid having to state requirements on values by making it implicit that the values supplied to a library component that imposes requirements via Concepts must be in the domain of the operations those Concepts require. E.g. passing a This approach works great for components like the standard algorithms that operate only on supplied values and do not synthesize new values. It may fail completely for components that do synthesize values like the contents of |
Resolution-in-progress. I will move this into the OP (and delete it from this post) when we're done with it. (To avoid confusion, the PR-in-progress that was here has been removed.) |
Hmm, does |
Yes it does. I was planning to increase the scope to cover all specializations whose function call operator results in a call to a builtin pointer comparison and didn't make the changes to para 2. I'll let discussion here guide whether I finish that process - so the semantics are consistent - or back it out to what C++17 requires and give up on these things until after the TS. EDIT: Specifically I mean that EDIT AGAIN: Ok, this is a terrible idea. I don't want to screw up our chances of getting the TS out the door in Toronto in order to make a point about how broken the comparison function objects are. Backing out... |
Backed out requirement to Added a requirement for |
Do we have any idea if |
We sort-of hacked something up when we went through this the first time. IIRC:
I honestly don't care. The worse it is, the easier it will be to convince WG21 to fix this mess in std2 by splitting |
The current wording (incorrectly) says |
The call operator is what must be equality preserving, not the "expression that we don't actually use but that lets these types end-run around the semantic requirements." I think "the call operator yields a strict total order" is sufficient to require that it be equality-preserving without extra verbiage. |
Yes, but the call operator can't possibly be a strict total order if the conversions-to-pointer aren't equality-preserving. I think we need to say that requirement somewhere? |
This is complicated by the fact that "if the call operator calls a built-in operator comparing pointers, the call operator yields a strict total order" implies that the "built-in operator comparing pointers" yields a strict total order. LWG 2562 completely contradicts the core language. EDIT: I think LWG 2562 actually wants "if EDIT 2: "if the call operator calls a built-in operator comparing pointers, the call operator yields a strict total order" admits the possibility that the call operator "calls a built-in operator comparing pointers," discards the result of that operation, and then performs the strict total order magic. However, the specification of the call operators is "*Returns: EDIT 3: If you squint at it sideways hard enough, you could claim that when the result of What we want this to say is something like:
but this needs to be specified as the behavior of the individual call operators, so maybe e.g. for
And for the other operators, delegate pointers to
The non-
|
... when the operation is well-defined. |
Not sure that works. What about types like: struct S {
operator void* () const;
}; Then |
Once again, the built-in Maybe we can simply add something like "for the purposes of this requirement, the built-in |
Enormous PR update (now in the first post). I've specified EDIT: Oh, and I removed the requirement that |
…ts of RandomAccessIterator Fixes #154.
In the requirement of
RandomAccessIterator<I>
requires thatI
must satisfyTotallyOrder<I>
. From the semantical requirement of 19.3.4 [concepts.lib.compare.totallyordered] p1, this requires that for all objectsa
,b
,c
of iterator type one of following is true:a < b
,b < a
,b == a
. Non normative note placed below there requirements, states that this is not required to be true for not well-formed object (default constructed pointers/iterators), but does not exclude compare pointer to different arrays (which are well-formed).Related question, does calling
std::max(begin(v), end(v))
on vector of pointers to same array is well-formed, or it is now ill-formed? In other words, are we allowed to call sort with the comparator that does not impose total order on all values of given type, but is total order if we limit domain to collection of object passed in invocation?Proposed Resolution
Modify the synopsis of the header
<experimental/ranges/functional>
in [function.objects] as follows:Also modify the detailed specifications of the comparison function objects in [comparisons]:
The text was updated successfully, but these errors were encountered: