Date: Thu, 25 Sep 2025 08:47:53 +0200
Is this not easily fixable by converting the:
constexpr T* operator->() {...}
constexpr T const* operator->() const {...}
with just:
constexpr T* operator->() & {...}
constexpr T const* operator->() const& {...}
?
The code I can, however, see break by this that is perfectly valid code is
code that uses std::optional<std::reference_wrapper> or similar.
On Thu, Sep 25, 2025, 07:53 Jan Schultke via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> > It would break lots, because we're not talking here about the built-in
> operator-> that you quoted; we're talking about the overloaded operator->.
>
> Yeah sure, you'd also (or instead?) need to adjust the way [over.ref]
> expands to the builtin -> operator.
>
> Now thinking about it, it's probably "instead" because whether a
> pointer is an rvalue is somewhat irrelevant. However, it's also
> irrelevant whether an iterator or any other type with reference
> semantics is an rvalue, so an operator-> that propagates value
> category should presumably have an explicit opt-in.
>
> > struct T {
> > int fn() &;
> > };
> > struct Optional {
> > T t_;
> > const T* operator->() const { return &t_; }
> > T* operator->() { return &t_; }
> > };
> > struct SharedPtr {
> > T *p_;
> > T *operator->() const { return p_; }
> > };
> > Optional getOptional();
> > SharedPtr getSharedPtr();
> > int main() {
> > getOptional()->fn(); // getOptional() is a prvalue, and we'd like
> fn() not to be callable here
> > getSharedPtr()->fn(); // getSharedPtr() is a prvalue, but we must
> continue to call fn() here; there's nothing wrong with this code
> > }
> >
> > Now, we could at least give a (QoI) compiler warning if `expr->f()` on
> an rvalue `expr` invokes a non-const `operator->` member function. (Test
> cases here.) That seems tricky to implement, but maybe it's not so hard as
> I'm thinking.
> > In fact, my P1144 Clang fork already gives a warning if `expr = rhs` on
> an rvalue `expr` invokes a non-const `operator=`, because that's almost
> always a bug.
>
> There's no way that in your code, the compiler could tell if you're
> working with a type that has reference semantics (like SharedPtr), so
> I don't think any of this can be solved as QoI. I don't see how it
> would be warning-worthy to call a non-const member function through
> operator-> on an rvalue like getSharedPtr(). The value category of the
> SharedPtr is irrelevant.
>
> > getOptionalRef()->set(2);
> > getSharedPtr()->set(2);
> > getPointer()->set(2);
>
> Code like this is totally fine.
>
> With the benefit of hindsight, operator-> should have returned an
> lvalue or rvalue reference rather than a pointer, exactly like
> operator*. Then the author of the class could choose to propagate the
> value category, only if that makes sense.
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
constexpr T* operator->() {...}
constexpr T const* operator->() const {...}
with just:
constexpr T* operator->() & {...}
constexpr T const* operator->() const& {...}
?
The code I can, however, see break by this that is perfectly valid code is
code that uses std::optional<std::reference_wrapper> or similar.
On Thu, Sep 25, 2025, 07:53 Jan Schultke via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> > It would break lots, because we're not talking here about the built-in
> operator-> that you quoted; we're talking about the overloaded operator->.
>
> Yeah sure, you'd also (or instead?) need to adjust the way [over.ref]
> expands to the builtin -> operator.
>
> Now thinking about it, it's probably "instead" because whether a
> pointer is an rvalue is somewhat irrelevant. However, it's also
> irrelevant whether an iterator or any other type with reference
> semantics is an rvalue, so an operator-> that propagates value
> category should presumably have an explicit opt-in.
>
> > struct T {
> > int fn() &;
> > };
> > struct Optional {
> > T t_;
> > const T* operator->() const { return &t_; }
> > T* operator->() { return &t_; }
> > };
> > struct SharedPtr {
> > T *p_;
> > T *operator->() const { return p_; }
> > };
> > Optional getOptional();
> > SharedPtr getSharedPtr();
> > int main() {
> > getOptional()->fn(); // getOptional() is a prvalue, and we'd like
> fn() not to be callable here
> > getSharedPtr()->fn(); // getSharedPtr() is a prvalue, but we must
> continue to call fn() here; there's nothing wrong with this code
> > }
> >
> > Now, we could at least give a (QoI) compiler warning if `expr->f()` on
> an rvalue `expr` invokes a non-const `operator->` member function. (Test
> cases here.) That seems tricky to implement, but maybe it's not so hard as
> I'm thinking.
> > In fact, my P1144 Clang fork already gives a warning if `expr = rhs` on
> an rvalue `expr` invokes a non-const `operator=`, because that's almost
> always a bug.
>
> There's no way that in your code, the compiler could tell if you're
> working with a type that has reference semantics (like SharedPtr), so
> I don't think any of this can be solved as QoI. I don't see how it
> would be warning-worthy to call a non-const member function through
> operator-> on an rvalue like getSharedPtr(). The value category of the
> SharedPtr is irrelevant.
>
> > getOptionalRef()->set(2);
> > getSharedPtr()->set(2);
> > getPointer()->set(2);
>
> Code like this is totally fine.
>
> With the benefit of hindsight, operator-> should have returned an
> lvalue or rvalue reference rather than a pointer, exactly like
> operator*. Then the author of the class could choose to propagate the
> value category, only if that makes sense.
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2025-09-25 06:48:08