[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.
–Arthur