-
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
Readable types with prvalue reference types erroneously model Writable #381
Comments
Solution 1A partial fix is to change (1.3) - Otherwise, if the types T1 and T2 of E1 and E2
satisfy IndirectlyMovableStorable<T1, T2>() &&
IndirectlyMovableStorable<T2, T1>(),
+ and if reference_t<T1> and reference_t<T2>
+ are lvalue reference types,
(void)(*E1 = ranges::iter_exchange(E2, E1)), except
that E1 is evaluated only once. That makes iterators that return things by value not satisfy One could argue that with the suggested change, (1.3) above is no longer particularly useful, so an alternative approach would be to simply delete that para. Either way, it would no longer be enough to hook However Solution 2Change template <class Out, class T>
concept bool Writable() {
return requires(Out& o, T&& t) {
*o = std::forward<T>(t); // not required to be equality preserving
+ const_cast<const reference_t<Out>&&>(*o) =
+ std::forward<T>(t); // not required to be equality preserving
};
} The reasoning here is that when This would also lose the ability to use Obviously, we would require that the two valid expressions of |
Solution 3Push for a language change where the compiler-generated copy/move assignment operator is (lvalue) reference qualified, changed all |
I think you're emphasizing the symptom over the actual problem, which is that prvalues of As such, I'm not certain this is the right answer, but I agree it is the right now answer. It addresses the usability problem for users of the TS. |
Prvalues of class types often lie about being mutable, yes. And I agree that that is the root cause of the problem. I can make that point more strenuously if you think it would help. I don't. STL2 has N decades of legacy code to contend with, where N >= 3. It simply must play well in that world.
For extremely large values of right now. ;-) |
Way to go, me - write the exact opposite of the point you're trying to make.
Agreed, but 20 or 25 years from now it would be nice if assignment operators were properly |
Data point: neither |
But not for any valid technical reason. They too represent an indirection. Assigning through these types doesn't change the bits of the proxy. Besides, I suspect these containers have iterators that fail to model |
... and c'mon. You have to admit that this is a very nice hack. :-) |
Also worth considering, I think, is the interaction between a const-qualified assignment operator and [res.on.data.races]/3. With the current direction, that wording will need to be weakened somehow sooner or later. |
The standard needs to properly define what "non-const" arguments means. E.g., can these functions: namespace std {
void f1(int* p);
void f2(int* const p);
} modify |
Incorporating the PR of #387, and correcting for the fact that "equivalent" in "The second expression in the above requires clause is equivalent to the first" has no well-defined meaning: Change the definition of template <class Out, class T>
concept bool Writable() {
- return requires(Out& o, T&& t) {
+ return requires(Out&& o, T&& t) {
*o = std::forward<T>(t); // not required to be equality preserving
+ *std::forward<Out>(o) = std::forward<T>(t); // not required to be equality preserving
+ const_cast<const reference_t<Out>&&>(*o) =
+ std::forward<T>(t); // not required to be equality preserving
+ const_cast<const reference_t<Out>&&>(*std::forward<Out>(o)) =
+ std::forward<T>(t); // not required to be equality preserving
};
} In [iterators.writable] paragraphs 2.1 and 3, replace "the assignment" with "either above assignment": (2.1) — If Readable<Out>() && Same<value_type_t<Out>, decay_t<T>>() is satisfied, then *o after
- the
+ either above
assignment is equal to the value of E before the assignment.
3 After evaluating
- the
+ either above
assignment expression, o is not required to be dereferenceable. |
"Either" is for denoting one of two alternatives. There are four assignments in the proposed resolution, so "either above assignment expression" is wrong. Maybe "after any of the above assignment expressions..."? |
* P0541 * P0547 * P0579 * ericniebler/stl2#155 * ericniebler/stl2#167 * ericniebler/stl2#172 * ericniebler/stl2#229 * ericniebler/stl2#232 * ericniebler/stl2#239 * ericniebler/stl2#241 * ericniebler/stl2#242 * ericniebler/stl2#243 * ericniebler/stl2#244 * ericniebler/stl2#245 * ericniebler/stl2#255 * ericniebler/stl2#286 * ericniebler/stl2#299 * ericniebler/stl2#301 * ericniebler/stl2#310 * ericniebler/stl2#311 * ericniebler/stl2#313 * ericniebler/stl2#322 * ericniebler/stl2#339 * ericniebler/stl2#381 Remove post-increment experiment in `move_iterator`. Remove `EqualityComparable`/`Sentinel<default_sentinel>` extensions to `ostreambuf_iterator`.
* P0541 * P0547 * P0579 * ericniebler/stl2#155 * ericniebler/stl2#167 * ericniebler/stl2#172 * ericniebler/stl2#229 * ericniebler/stl2#232 * ericniebler/stl2#239 * ericniebler/stl2#241 * ericniebler/stl2#242 * ericniebler/stl2#243 * ericniebler/stl2#244 * ericniebler/stl2#245 * ericniebler/stl2#255 * ericniebler/stl2#286 * ericniebler/stl2#299 * ericniebler/stl2#301 * ericniebler/stl2#310 * ericniebler/stl2#311 * ericniebler/stl2#313 * ericniebler/stl2#322 * ericniebler/stl2#339 * ericniebler/stl2#381 Remove post-increment experiment in `move_iterator`. Remove `EqualityComparable`/`Sentinel<default_sentinel>` extensions to `ostreambuf_iterator`.
Consider:
This is a huge usability problem, since it permits users to, e.g., pass a range to
sort
that is not, in fact, sortable. The Ranges TS inherited this problem from the Palo Alto Report (N3351), which also has this bug. Fixing this will betrickyeasy :-) without messing up proxy iterators.EDIT: See ericniebler/range-v3#573
Resolution Discussion:
One fix would be to require that
*o
return a true reference, but that breaks when*o
returns a proxy reference. The trick is in distinguishing between a prvalue that is a proxy from a prvalue that is just a value. The trick lies in recognizing that a proxy always represents a (logical, if not physical) indirection. As such, adding a const to the proxy should not effect the mutability of the thing being proxied. Further, ifdecltype(*o)
is a true reference, then addingconst
to it has no effect, which also does not effect the mutability. So the fix is to addconst
todecltype(*o)
,const_cast
*o
to that, and then test for writability.Proposed Resolution
Accept the wording in P0547R1 (duplicated below).
(Includes the resolution for #387.)
Change the definition of
Writable
([iterators.writable]) as follows:In [iterators.writable] paragraphs 2.1 and 3, replace "the assignment" with "either above assignment":
The text was updated successfully, but these errors were encountered: