Paper suggests rewriting `ptr->meber` by `(*ptr).member`.
But this creates an interesting case, new `->` created this way will
have capabilities impossible compared to manually written `->`.
Yes, and
P3039R1 mentions that it does [...]
[...]
Informally, yes, of course. P3039's adding rewrite rules for `->` is exactly analogous to how C++20 added rewrite rules for `!=`. It doesn't mean that defining your own `operator!=` is somehow morally bad at this point, but it does mean that we expect defining your own `operator!=` to become much rarer, and certainly unidiomatic in modern C++. However, because of the billions of lines of code out there that do define their own `operator!=`, it wouldn't be sensible to try to "push" anyone off of it (e.g. by deprecating the syntax or anything like that). We can afford to let the newer cleaner style displace the older more verbose style in its own natural time.
For contiguous iterators (and any other iterators that want to work with std::to_address) the rewrite is undefined for a past-the-end iterator.
My understanding is that P3039 does not propose to rewrite any explicit member-function-syntax call to `operator->`, such as:
T *p = sptr.operator->();
into:
T *p = &*sptr;
Right now, std::to_address() is
defined in terms of such an explicit member-function-syntax call to `sptr.operator->()`. So, any "fancy pointer" or "smart pointer" will not be able to take advantage of this new feature; they'll basically have to keep providing a user-defined `operator->()` member function — or else specialize `pointer_traits` for their type (which IMO they really shouldn't do, no more than they should specialize iterator_traits or allocator_traits).
It seems lucky, and coincidental, that smart pointers and fancy pointers are precisely those things where the value category of the thing doesn't affect the value category of its referent: `sptr->x` and `std::move(sptr)->x` should probably always have the same value category.
C++20 fuddles this up a little bit by conflating "contiguous iterators" with "fancy pointers" — making `std::to_address()` the blessed way to extract a past-the-end pointer from a past-the-end contiguous iterator. The writer of a contiguous iterator will need to support std::to_address(), which means supporting member-function-call syntax for `it.operator->()`, which means not getting to use the rewrite feature for contiguous iterators. This is Too Bad; but, unless I've missed something in spelling it out in this paragraph:
- it doesn't seem too horrible, and
- IMO if we want to fix the contiguous-iterator-cast-to-pointer issue, we would do better to revisit std::to_address and do better this time around (e.g. just allow it to use `static_cast<T*>(it)` when that's well-formed, and/or give it a Ranges-inspired customization point such as `it.base()` or `it.to_pointer()`). Letting the current messiness of `std::to_address` interfere with the current cleanliness of P3039 would be letting the tail wagging the dog.
P3039 §6.6 says "20.2.4 [pointer.conversion] specifies to_address in terms of calling p.operator->(),
so some thought will need to be put in there on what to do." IMO the thing to do there is
nothing, for now.
§6.6 already gives nothing, for now, as option (1), and characterizes the outcome reasonably accurately:
1 and 2 feel like the wrong approach – they would mean that authors of iterator types still need to define their own operator->, or they must specialize some class template (if we agree that the current semantics with regard to iterators are correct), or they must overload to_address and we make that a customization point found by ADL.
I would insert the word "contiguous" before "iterator types," and I would say that 1 feels like exactly the right approach.
P.S. — P3039 claims that "The following standard types can be used to instantiate `pointer_traits`: [...] `span`," before pointing out correctly that pointer_traits<span<T>> is nonsensical and doesn't work. This makes me wonder: Why do the authors of P3039 think that `span` can be used to instantiate `pointer_traits`? Who's teaching that? Certainly a `span` is not a pointer — it's not even dereferenceable. If this is implied by something in the actual paper Standard, we should probably fix that thing ASAP.
–Arthur