-
Notifications
You must be signed in to change notification settings - Fork 252
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] forwarding a member of class from method with const this
returns non-const reference that drops const
#274
Comments
Maybe this is also a good example for |
Good points, thanks! Re Re Thanks! |
Manually citing 413de0e here since I mistyped "closes $274" in the title... it's been a long day :) |
Regarding In situations where we return member of the class definately |
Now my code changed from: get_name: (this) -> forward const std::string = this.name;
get_description: (this) -> forward const std::string = this.description;
get_config: (this) -> forward const execspec::config_t = this.config;
get_stages: (this) -> forward const std::vector<execspec::process_stage> = this.stages; to get_name: (this) -> forward _ = this.name;
get_description: (this) -> forward _ = this.description;
get_config: (this) -> forward _ = this.config;
get_stages: (this) -> forward _ = this.stages; Looks promissing! |
And @TartanLlama is using |
Mmm, but those references remind me that we'll now be returning a dangling reference for return of a local variable. I should ban that... ok, done! |
See 43cdf3e |
Re these:
Is the
should work too, right? |
Yes. A difference is that the former necessarily breaks when a data member declaration is renamed but not its use. The latter may continue to work and change meaning. |
@hsutter yes, that works. I did not know that it will work. I thought that I just checked. Deducing this (P0847) described also here https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/#design requires use of From the blog mentioned above:
struct cat {
std::string name;
void print_name(this const cat& self) {
std::cout << name; //invalid
std::cout << this->name; //also invalid
std::cout << self.name; //all good
}
}; Should we align to that? |
[updated answer]
If you mean should we ban using After I wrote that I realized maybe you meant should we require qualifying |
I think |
@hsutter Yes, I was referring to requiring qualifying
P0847 is similar here: when the method defines |
@AbhinavK00 regarding forwarding take a look here: #248 I will provide more info later. |
"Should we require I'm not yet convinced about requiring
(*) But this did motivate me to make a change: Let's try improving shadowing by banning member name shadowing. One of the reasons you really need the ability to qualify Summarizing, I'd say that in terms of solving known problems:
Yes, and you can still do that and I agree that's important. There's a snapshot of my current thinking about, anyway. Again, good question! Thanks. |
I personally think that
Defaults are still kind of magic. In case of
Isn't that what we do today? We write
One problem I can think of having to write
This can go both ways, not having to write I think in this case, sticking to explicit By the way @filipsajdak , I understand forward returning, it's just forward parameters that I'm unable to see use case for (other than wrapper functions). |
@hsutter I agree that having to use this.fun(x,y); will trigger UFCS and will end up with: CPP2_UFCS(fun, (*this), x, y); There is no issue when there are member functions but if you will make mistake you can call external function instead. So, we need to pay more attention how it will interact with UFCS as that might not be obvious. And it makes currently difference if you will call a method with or without |
@AbhinavK00 argument forwarding are useful for constructors and factory methods when you want to forward the type you provides as an argument (its called perfect forwarding - see here: https://en.cppreference.com/w/cpp/utility/forward#Example). In cppfront Herb shows how to perfect forward a specific type using requires clause that makes it even better (see here: https://youtu.be/ELeZAKCN4tY?t=5330). |
Right. It's generally true that Good observation, thanks. |
It's a way to provide customization points. |
That should be reserved for the type's author, right? Or the namespace's owner. |
Right, only the type's author can use |
Even if you allow the reopening of types? (like you need for metaclass style namespaces?)
On 13 March 2023 20:57:37 Herb Sutter ***@***.***> wrote:
Right, only the type's author can use this. qualification because only they can write type-scope (member) functions and have a this.
—
Reply to this email directly, view it on GitHub<#274 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQLTOYUKYXIF3R5ED33W36C33ANCNFSM6AAAAAAVXWMTCY>.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
…e oddity `inout: const` Closes #876 This keeps things nicely orthogonal addresses the two open issues with parameter passing: - the oddity of `inout : const`, which becomes just `in_ref` - the need to directly express both Cpp1 `-> decltype(auto)` and `-> auto&&`, which now are `-> forward _` and `-> forward_ref _` Style Parameter Return -------- --------- ------- `in` ⭐ `inout` ✅ `out` ✅ `copy` ✅ `move` ✅ ✅ `forward` ✅ ⭐ The two ⭐'s are the cases that automatically pass/return _by value_ or reference: - all uses of `in` - deduced uses of `-> forward _` (ordinary `-> forward specific_type` always returns by reference, adding constness if there's an `in this` parameter) Now both ⭐ cases also provide a `_ref` option to not choose by-value. This addresses the two open issues with parameter passing: An example that illustrates both is std::min: min: (in_ref a, in_ref b) -> forward_ref _ = { if b < a { return b; } else { return a; } } main: (args) = { x := 456; y := 123; std::cout << min(x, y) << '\n'; } The container<T>::operator[] case could already be written like this, where the first return lowers to Cpp1 `T const&` and the second to `T&`: container: <T> type = { buf: std::array<T, 10> = (); operator[]: ( this, idx: i32) -> forward T = buf[idx]; operator[]: (inout this, idx: i32) -> forward T = buf[idx]; } main: (args) = { v: container<int> = (); std::cout << v[0] << '\n'; }
In the current implementation (a981011) cppfront does not handle forwarding of class attributes well. The following code:
Generates:
When compiles using cpp1 compiler:
Expectation
I expect to get
std::string const&
when forwarding the member of the class whenin this
is used (explicitly or implicitly). I can now achieve that by adding const to the return type:that will generate:
That works but breaks the idea of writing intentions rather expected generated code.
Side notes
Playing a little bit with that I come to the conclusion that what I am missing here is something that will mimic Deducing this (P0847). I imagine writing something like the following:
I understand that P0847 is a C++23 feature that is not yet implemented by our supported compilers but maybe we can mimic it here somehow? Using that syntax we could at least generate both versions of methods (const & non-const).
I am also a little disapointed with version that returns
forward _
. The following code:Generates:
In both methods return type is deduced to
std::string
. What I was expecting was behaviour like with return typeauto&&
which for non-const version returnsstd::string&
and for const version returnsstd::string const&
.The text was updated successfully, but these errors were encountered: