[SUGGESTION] Add the equivalent of C++23 "deducing this" #1197
Description
For the same motivations as in P0847, I'd like to be able to write the equivalent of the "deducing this" feature in Cpp2.
Here's one example where the feature would prevent duplicating code:
my_optional: <T> type = {
has_value: bool = false;
value: T;
get: (in this) -> forward T = { assert(has_value); return value; } // return const-ref
get: (inout this) -> forward T = { assert(has_value); return value; } // return mutable-ref
get: (move this) -> move T = { assert(has_value); return value; }
}Instead I'd like to write the get function once, with some kind of equivalent "deducing this" syntax that takes care of the this parameter and also the return type, e.g. along the lines of:
get: <Self>(Self _ this) -> _ = { assert(has_value); return value; }The lowered C++ could duplicate the functions if it's targeting C++20, since the C++23 "deducing this" syntax won't be available.
If, in the future, cppfront supports some kind of cpp_standard flag (like proposed in #942) then the lowered code for C++23 could use the real "deducing this" feature.
Will your feature suggestion eliminate X% of security vulnerabilities of a given kind in current C++ code?
No
Will your feature suggestion automate or eliminate X% of current C++ guidance literature?
Yes, in the same way that "deducing this" does for C++.
Activity
SebastianTroy commentedon Aug 1, 2024
bluetarpmedia commentedon Aug 1, 2024
Absolutely, I don't mind what the syntax is, as long as it can lower either to C++23 deducing this or C++20 manually stamping out the variations.
Here's another idea:
get: (deducing this) -> _ = {...}😆
dutkalex commentedon Aug 2, 2024
@bluetarpmedia Just to be sure: are you saying that we can't use the "deducing this" feature as of now, or are you just advocating for a more direct and explicit syntax to ease the use of the feature?
Btw, I really like your proposed syntax. It doesn't get more explicit than that 😂
bluetarpmedia commentedon Aug 3, 2024
Currently cppfront lowers to C++20 so to the best of my knowledge there's no way to write Cpp2 that emits the C++23 "deducing this" feature, nor emits the various overloads to simulate it in C++20.
hsutter commentedon Aug 3, 2024
What we did for
"\x{62}"literals was to enable authoring them in Cpp2, but they would only work if your Cpp1 compiler supported them. If we keep doing this there'll need to be some documentation about what's supported and/or possibly a-std=switch to diagnose uses of things not supported in that mode.zaucy commentedon Aug 3, 2024
I think the syntax for explicit object parameter (deducing this) could simply be used whenever
thishas an explicit type.lowered to
Then any use of
this.could lowered tocpp2_self.inside a function using "explicit this". Maybe even requirethis.for these functions?As for detection we could simply use the feature-test macro
__cpp_explicit_this_parameterat the top of the file if there is any use of "explicit this".AbhinavK00 commentedon Aug 3, 2024
What I have is more of a question. As far as I understand, the deduplication of cpp code due to deducing this is because we can do perfect forwarding with the 'this' parameter.
In cpp2, we use the 'forward' passing convention to do forwarding. So why does the following not work/what is the difference in behavior
get: (forward this) -> forward T = {...}hsutter commentedon Aug 3, 2024
Thanks for the suggestions!
For a
thisparameter, Cpp2 currently disallows exactly (a)forwardand (b) an explicit type... and yes, it sure does seem like that may be exactly the design space needed for deducingthis? That's quite a convergence! -- I didn't pay close attention to the deducing-this feature's progress, and didn't design the Cpp2 restrictions onthiswith it in mind.It's easy enough to still allow code in the function body to consistently write
thisto refer to the parameter, even if we emit it asthis_orselfunder the covers to satisfy Cpp1 requirements in C++23.Here's an adaptation of the first two examples from cppreference, which I think covers the core use cases...
Note that Cpp2 already makes
forwardparameters be implicit templates, so something like this seems natural? In addition, unlike Cpp1, this would alsostd::forward<Self>(self)from the last use to preserve the cv-qualification and value category ofthisobject.Something like that, perhaps?
hsutter commentedon Aug 3, 2024
The above would just allow
forward thisto have this meaning, and doesn't yet allow actually specifying a type. Are there any examples not covered by the above that would also require specifying a type for athisparameter?Update to answer my own question: Yes,
forward this: specific_typewould be a reasonable thing to do, and that type should be limited to being the enclosing type's name, I think.bluetarpmedia commentedon Aug 4, 2024
This (no pun intended) sounds promising! One thing I'm wondering about is a function that returns
auto&&in C++. For example, how would we write the Cpp2 to emit the following example from Sy Brand's blog post?I think this function signature would work for constexpr and returning
auto&&:value: (forward this) -> forward _ == { ... }But how would we write the equivalent for this line?
return std::forward<Self>(self).m_value;Would it be the following?
JohelEGP commentedon Oct 4, 2024
Yes.
It can be useful to use pass the (possibly derived) deduced type to
std::forward_like.The paper, P0847, has an example.
The
mainbranch already knows that the type-idSelfnames a template parameter,so it can known
thisis in fact a forwarding parameter.Although that doesn't seem to be currently rejected (see https://cpp2.godbolt.org/z/vr63W6e46).
This is very similar to #572 (comment).
It seems like deducing
thisalready allows you to placethis's type: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html#sfinae-friendly-callables.There might be a conflict by using
forward thisfor deducingthisif Cpp1 gets the other feature.JohelEGP commentedon Oct 4, 2024
I've always wanted this.
I first mentioned it at 4bd0c04#commitcomment-133307333.
The second comment has an implementation that errors when deducing
thisisn't supported (on use ofthis).A quirk with a deducing
thisCpp2forward thisparameteris that, suddenly, you need to
this.-qualify uses of members.The need isn't as apparent in the Cpp2 syntax as in the Cpp1 syntax.
Is it the lack of an explicit name like in Cpp1 (conventionally
self)?JohelEGP commentedon Oct 4, 2024
Deducing
thisalso allows declaring the object parameter by-copy.From https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html#by-value-member-functions-for-performance:
We could also allow a
copy thisparameter to mean that by lowering to Cpp1this current_type self.But what if you also want the
forward thissemantics above?Do we add
forward_copyfor that?JohelEGP commentedon Oct 4, 2024
Let's be more clear.
#572 (comment) says that the current
mainbranch implements from P2481:Today's
f: (forward x: of_my_type) = { ... }only requires convertibility toof_my_type.The part of P2481 cppfront doesn't (yet) implement is making
xbeof_my_type.Including
f: (forward x: std::variant) = { ... }deducing from derived types but converting to thestd::variantbase.For P0847 (deducing
this, explicit object member functions),Herb proposes the natural spelling of
mem: (forward this).Now, where both of these features would meet is in
mem: (forward this: of_my_type).It seems to me that both P2481 and P0847 would give it the same meaning.
P2481 would make
this's type beof_my_type, deducing constness and reference, accepting derived types.P0847 would do the same, and an explicit object parameters accepts derived type conversions as usual.
If I'm analyzing this right, these features are not actually in conflict.
So sharing the same syntax (i.e., when a
forwardparameter is forthis) should be fine.JohelEGP commentedon Oct 5, 2024
Actually,
forward_copydoesn't make sense.But both
copyandforwardthisparameters share behavior from P0847.this.qualification.current_type.f: (copy this) = { ... }should be made to work.f: (copy this: derived_type) = { ... }should be made to work (https://cpp2.godbolt.org/z/oMjPbv3ro).f: (forward this) = { ... }should be made to work (forwarding parameter tocurrent_type).f: (forward this: _) = { ... }should be made to work (generic forwarding parameter).Additionally, from P2481:
f: (forwardthis-or-name: X) = { ... }should be made to work, coercing the argument to anX.What we already have from P2481 (as Cpp2 features):
f: (forwardname: _) = { ... }itself.f: (forwardname: specific_type) = { ... }as something close tof: (forwardname: _ is std::convertible_to<specific_type>) = { ... }.Although I'm wary of how it's currently implemented.
Combining these two features, we get:
f: (forward this: derived_type) = { ... }.Require '=' for all funcs, default single-expr bodies to `-> forward _`