-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
F.18: Confused about difference between "will-move-from", "forward" and "in" #1407
Comments
The answer for this example seems pretty obvious. It's not an "in" parameter, because you're not just providing a value as read-only input to the function. It's not a "forward" parameter, because the function is making use of it not just forwarding it to another function. It's obviously a "will move from" parameter, because you're moving from it! You could write the return statement as
which should make it even more obvious. F.18 says the parameter should be This doesn't seem like a compelling case for additional definitions. |
To write code that is in accordance with the guidelines you can look at the enforcement rules. If your code would be flagged by a checker that enforces the rule, it's not in accordance. Passing |
Not at all disputing that F.18 applies here (as I said, I don't quite understand the guidelines as written), but I still think it would be helpful to clarify the terms. What if I had written it like this? struct A {
vector<int> data;
};
struct B {
vector<int> data;
};
B convert(const A& a) {
return B{a.data};
} In this case, I didn't move from it, so it's not "obviously" a will-move-from parameter. |
It's obviously not a will-move-from parameter if you aren't moving from it. I'm struggling to see what the problem is. The type of parameter is dictated by what the function does with it. If you want to modify the parameter (e.g. by moving it) then obviously don't pass it by reference-to-const. F.16 in particular has been standard practice for decades, and is just common sense. If you don't want to modify it (e.g. just copy it as in your second example) then don't pass by value unless it's cheap to copy (like an F.18 deals with cases where you might be trying to choose between defining |
Thanks for your help with this - sorry I'm not doing a good job of adequately explaining my confusion - confusion is by nature hard to explain. I really do appreciate your patience in bearing with me. I'm now starting to believe that perhaps I now understand the guidelines as written, but I wish the guidelines offered more guidance about how to author functions/overload sets such as You mentioned, "the type of parameter is dictated by what the function does with it", however I don't think it's that simple: if I'm trying to write a new function/overload set, I can't simply look at what it's doing with the parameter, because the function doesn't exist yet. In the specific case of Based on my current (still not totally clear) understanding, the following are all valid ways to write Choice A: input is an "in parameter"This is covered by the chart above F.16 in "normal parameter passing", and in the rule F.16 if I decide I'm not in an "advanced use". B convert(const A& a) {
return B{a.data};
} Choice B: input is an "in parameter" but I want to optimize it for advanced usesThis option is called "in & retain copy" in the chart above F.16 in the "advanced" section. B convert(const A& a) {
return B{a.data};
}
B convert(A&& a) {
return B{std::move(a).data};
} Choice C: input is a "will-move-from" parameterThis is covered by F.18 and "in & move from" in the chart in the advanced section. B convert(A&& a) {
return B{std::move(a).data};
} Choice D: input is a "forward" parameterThis does not appear on the chart, but seems to be allowed by F.19 template <typename A>
B convert(A&& a) {
return B{std::forward<A>(a).data};
} So, based on my current understanding of the guidelines, all four of these are in accordance. However, my goal as an implementor of I guess, getting more specific, the thing that is confusing me is what seem to be overlapping guidelines for "in & retain copy" (F.16) and "in & move from" (F.18) - it seems that the cases where these are appropriate are really, really similar. The only situations I can think of where one would be appropriate but not the other is when a type is either slow-to-move or noncopyable. Most of the time, if one is appropriate, they both would be. So, how do I as the implementor of a function decide to follow the "in & retain copy" guideline (in which case I provide separate If you're asking me (which I know you aren't!), the best option for when you want to retain an argument like this is providing only a |
I agree. And I also agree that there's little difference between some of the options you show for this example, and that the choice may be fairly arbitrary (or based on other concerns not described by F.16 etc). As I see it the main point of the guidelines is to say don't do one of these versions:
Any of your almost-equivalent options would be OK, but these versions would not be. Maybe the confusion was just that you've already internalized similar rules to F.16 etc. and were looking for expert-level advice, when actually they're trying to offer beginner- and intemediate-level advice. |
Thanks so much for the help. Sounds like we're agreed on the facts, and I totally agree that the guidelines as written do provide value as they disallow some non-optimal approaches that you've laid out. I'm still feeling that the choice between providing both Points in favor of only a
Points in favor of both
|
Another thing that could have helped resolve this confusion earlier for me is a note somewhere that the guidelines provide a lot of freedom on how to write functions. In particular, the chart above F.16 appeared to me to be a sort of decision tree on how to write an overload set; but however as we've discussed there's still plenty of freedom given in exactly how to take parameters. |
It is there, tucked away in F.16: For advanced uses (only), where you really need to optimize for rvalues passed to "input-only" parameters:
That's a good suggestion. Could you submit a pull request? |
Sure! I'll get to it in the next few days.
Hrm. Maybe I'm still a bit puzzled. Going back to my |
Editors call: Thanks, looking forward to the PR. |
As I discussed in isocpp#1407, I found the existing wording confusing as I expected that the guidelines would provide me with one recommended way to author function arguments in all situations. However, the discussion on the issue clarified that the intent was to allow many ways to author functions, and only to disallow specific clearly non-optimal cases. This is my attempt to reduce that confusion, by explicitly calling out when such freedoms exist.
Hi! I'm writing in the hope that we can can clarify the cases in which F.16 applies vs. the cases in which F.18 or F.19 applies. Without having the terms "forward", "will-move-from", and "in" defined, I'm honestly not sure what the guidelines are recommending in certain cases. Just as one example, consider the following toy:
Here, which rule applies to the parameter
a
inconvert
? Is it an "in" parameter? Is it a "will-move-from" parameter? Is it a "forward" parameter? I'm not sure how to make the distinction, and if pressed I could create arguments for all three options. This is important because without understanding which case we're in, it's impossible to know how I'm supposed to write theconvert
function in accordance with the guidelines.Please note, I'm not asking for help specifically writing this
convert
function, this is just one example. I'm confused about these guidelines in numerous other cases as well. I think for this set of guidelines to be truly useful to me, I need actual definitions for "in", "will-move-from", and "forward".The text was updated successfully, but these errors were encountered: