I have previously thought about the idea of the operator() auto supplied return type and implied return value (IF and only iF omitted) as your original idea started with.  The implied return value (aka *this) (IFF omitted) would be just like main() having the implied return 0; (IFF omitted).  I would need to hear other people's arguments pro or con but am open to it.  There might be a hidden gotcha that I have not thought of.

[edit: just found the funniest thing,
You can not do: 
struct A {
    virtual auto                 operator++()   { return *this; }       //"error: virtual function cannot have deduced return type"
//or
    virtual decltype(*this) operator++()   { return *this; }        // error: invalid use of 'this' at top level
};
but you can use auto and decltype(*this) if you use the training return type.  Bonus: the return type honors the function constness like you wanted.
struct A {
    virtual auto operator++()     const -> decltype(*this) { return *this; } 
    virtual auto operator++(int)           -> decltype(*this) { return *this; }
};
so half of it can be done, the compiler knows. Now to just make it implied.
end edit]

My suggestion is just forget using the argument of an optimization and just keep the original idea,  operator() auto supplied return type and  operator() implied return value.  I think that anything else is just too problematic and confusing and confuses the issue.

Typically the standards committee does not like suggesting or requiring optimizations.  The compiler implementers can optimize it away, if they like, if they don't have an issue with it.

Your idea also ONLY works IF you can see the internals of those methods.  The implementation might not be in the header and might return something other than you expected.

Your idea about presuming the address does not change would fail with multiple inheritance even though you're still dealing with the same object just a different base or the derived type itself, the address is potentially different.



On Fri, Apr 7, 2023 at 5:36 PM Jan Schultke <janschultke@googlemail.com> wrote:
>> This proposal only prevents re-virtualization in call chains, which
>> means you are free to write:
>> devirtualized.a().b().c()

> It can not prevent re-virtualization in call chains
> It would be wrong to break the polymorphic mechanism just because it is in a call chain.

But why would that not be possible? I see how the Compiler Explorer
example breaks the call chains down into these temporary references
that are re-virtualized, but we can prevent that.

A "this" return type guarantees that not only the return type is the
same as the type of the called object, but the returned reference also
refers to the called object. It acts like std::integral_constant in
the sense that the type conveys value information.

> auto& unnamed1 = c.Fun3();
> auto& unnamed2 = unnamed1.Fun2();
> auto& unnamed3 = unnamed2.Fun1();

With a "this" return type, we can turn this sample into:

auto& unnamed1 = c.Fun3();
__builtin_assume(std::addressof(unnamed1) == std::addressof(c));
auto& unnamed2 = unnamed1.Fun2();
__builtin_assume(std::addressof(unnamed2) == std::addressof(unnamed1));
auto& unnamed3 = unnamed2.Fun1();

Although we would have to guarantee that not only the address is the
same, but also that the dynamic type was not altered by ending and
beginning the lifetime of c in any of the member functions.