Date: Fri, 7 Apr 2023 19:35:14 -0700
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_at_[hidden]>
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.
>
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_at_[hidden]>
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.
>
Received on 2023-04-08 02:35:27