On Thu, Sep 25, 2025 at 1:52 AM Jan Schultke <janschultke@googlemail.com> wrote:
[Arthur wrote:]
> 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.

Right, but the compiler can tell whether you're dealing with a pointer-semantic SharedPtr or a value-semantic Optional. That's the easy part. Value-semantic Optional preserves constness:
>   const T* operator->() const { return &t_; }
>   T* operator->() { return &t_; }
while pointer-semantic SharedPtr does not:
>   T *operator->() const { return p_; }

That "easy part" is the part I was pointing to already having solved in Clang, when (in my previous message) I pointed to the warning
<source>:13:14: warning: possibly unintended assignment to an rvalue of type 'Obj' [-Wassign-to-class-rvalue]
   13 |     getObj() = 42;
      |     ~~~~~~~~ ^

The "hard part" is keeping track of that information all the way until we see what you're doing with the ultimate `T` object and whether that operation is "OK" to do on rvalue-ish things. Operations like assignment and `fn() &` are "not OK" to do on rvalue-ish things.
Another thing that's "not OK" to do on an rvalue-ish thing, btw, is to discard its value without using it.


> getOptionalRef()->set(2);
> getSharedPtr()->set(2);
> getPointer()->set(2);

Code like this is totally fine.

Yes, but getOptional()->set(2) wouldn't be.

Cross-thread: I'm in favor of P3039 "Automatically generate operator->" too. But it needs wording, and an implementation of that wording, before it can go anywhere.

–Arthur