-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
doc: formalize non-const reference usage in C++ style guide #23155
doc: formalize non-const reference usage in C++ style guide #23155
Conversation
We generally avoid using non-const references if not necessary. This formalizes this rules by writing them down in the C++ style guide. Refs: nodejs#23028 Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hoping we can discuss this further, and find middle ground.
Quoting from previous PR: I believe non-const reference has been established as the tool to use for idiomatic iterative mutation in "modern" C++ (C++11 and higher). I'm taking the lead of the C++ ISO committee and the STL designers who decided to use references, and value semantics ahead of pointers. Case in point
From the C++CG:
With code that came up in a code review in our repo, it was presented with a common classical way to do mutation without a reference: for (std::string::size_type i = 0; i < name.size(); ++i) {
if (name[i] == '_')
name[i] = '-';
} Compared this with the C++11 idiomatic (read "this is iterating using a reference because the intent is to modify"). for (auto& i : name) {
if (i == '_')
i = '-';
} So essentially IMHO having "no non-const references" rule, means "no idiomatic mutations". From a personal perspective, I also agree with a few more arguments in favor of non-const reference:
BTW, both guides do have limits, spesificly WRT to function arguments:
|
P.S. Using as more standard guidelines will allow us to introduce automatic tools ( |
@refack would you be for making the rule more specific? i.e. only avoid non-const references in function parameters? |
P.S. I forget to mention the thing that is most important to me: learning material. IMHO it is immensely important for our code and guidance to be in line with the available learning material, to allow the inclusion of newcomers (or relearning late comers like myself) |
Yes. Something like "non const reference function argument means in/out parameter. Avoid in/out parameters" reads very clear and sound to me. |
I’m heavily -1 on that. For in/out, we should keep using pointers, since it’s otherwise completely invisible at the call site that it’s an in/out parameter.
That’s true, yes – It’s a feature that pointers lack.
I don’t understand this – they are references, not variables themselves in the classical sense? Whatever the reference refers to could still be some kind of null/default value.
That’s fair, although we are slowly moving a lot of our C++ code to use smart pointers, which means that on the reverse side regular pointers would typically be non-owning pointers.
I doubt that, esp. the “have to allocate space for it”. Under the hood, references are pointers, and code using one type should always compile to the same assembly as code using the other type. Can you provide a reference for this claim?
Hm – not sure I can judge this well myself, but I’m surprised by that since they are essentially the same concept, up to minor differences (no arrays using references, no default value). @refack It’s not that I don’t understand or don’t see your points – I have used references in my C++ code a lot more before coming to the Node.js world, and had to get used to the change myself. I fully acknowledge that that took a bit of learning when writing C++ code (with a lot of other things as well, such as variable/function naming conventions etc.). However, I’m also carrying my reviewer hat a lot of the time, and since Node.js is a mature software which millions of people use in their daily lives, I tend to value readability and more easily visible correctness higher than the ease of writing code. That’s a tradeoff, and it’s fair to stand on either side of it.
for (auto& i : name) {
if (i == '_')
i = '-';
} Note that I’m not saying that I’m for categorically excluding these types of constructs. However, I still hold my opinion that this code obscures the fact that |
@nodejs/tsc PTAL |
CPP_STYLE_GUIDE.md
Outdated
|
||
private: | ||
std::string foo_string_; | ||
int* pointer_to_integer_; // Pointer instead of reference. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally in cases like this I'd prefer to use a normal int here and return references because it's more memory-efficient (and if this is storing a pointer to mutate it the ownership seems to be pretty hard to figure out..)...maybe a member object with a made-up class here would be clearer? Or a unique_ptr
since that's what we generally try to refactor to? (Or a shared_ptr
but we don't seem to use it much in our codebase and try to restructure the ownership towards a unique_ptr
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 … How about e9f8406?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
See also additional rationale in https://google.github.io/styleguide/cppguide.html#Reference_Arguments, which is followed by V8 and Chromium.
It would seem like the reason for applying the If we're voting, here's how it looks so far: YES: NO: ABSTAIN: UPDATE: This passes. (19 TSC members w/ 6 abstentions = 7 votes required to pass.) |
CPP_STYLE_GUIDE.md
Outdated
|
||
private: | ||
std::string foo_string_; | ||
// Pointer instead of reference. If this objects 'owns' the other object, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@thefourtheye Thanks for catching, done!
CPP_STYLE_GUIDE.md
Outdated
private: | ||
std::string foo_string_; | ||
// Pointer instead of reference. If this objects 'owns' the other object, | ||
// this should be be a `std::unique_ptr<OtherClass>`; a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: double be
I abstain. |
I abstain |
@addaleax can we converge on "no non-const references as arguments in APIs"? Avoid defining APIs with non-const reference argumentsIn general, non-const references should only be used when they don't hurt readability, or if they are required by external APIs (e.g. std::for_each) Non-const references arguments make most sense as in/out parameters [F.11]. The in/out parameter pattern is non-optimal, and should be avoided. If the parameter is a regular (in) parameter, it should be const. If in/out semantics is necessary, use a pointer as per the Google style guide. |
@refack But this isn’t just about functions/methods…
This seems a bit confusing – why would we first mention that one thing makes sense, and then that we don’t do that because the Google style guide overrides it? |
I'm looking for some compromise on your part... |
@refack I’d prefer to avoid having this exact same discussion again about the other parts, though. Do you agree with the examples in this PR? |
That's why I asked for a compromise... I don't see anything in the examples code that conflicts with the paragraph I suggested. I don't agree with the comments and the generalization that "A pointer is almost always a better choice." |
As far as I can tell, that comment asked for documenting this in the style guide for only a subset of the use cases – That’s not really what a compromise is. (And, to mention it again: This PR documents current, existing rules.)
Yes, but your suggestion would leave the question open for a wide range of use cases (e.g. local reference variables). Those cases would otherwise be pointed out during reviews, which is less than helpful if we can also document them in our style guide for.
They both have advantages and disadvantages, so, yes, of course this depends on the use case. The big disadvantage of references, and how that can be mitigated, is mentioned here in the PR text. If you want, I can add something like “If you use non-const references, e.g. because of an external API that uses them, consider adding a comment that indicates which objects can be modified through them.” |
@addaleax and @refack (and most of the c++ world?) agree that non-const ref args are to be avoided. @addaleax had a short paragraph elaborating why, @refack had a longer more detailed paragraph covering the same territory. More detail doesn't seem bad. I genuinely don't understand what's going on here. "If in/out semantics is required, it is advised to use a pointer instead" and "a pointer is almost always a better choice" say the same thing, since in/out semantics is almost always why a ref arg is not const. |
@sam-github The difference here is that @refack’s text only refers to function arguments, while we currently have this as a more general rule that also applies to e.g. local variables or class members. |
Reviewed the comment thread again, and I don't see that difference in opinion. The only example Rafael showed of sane non-const refs was a for loop, which you have an example of in your code and also list as a reasonable use. If you amend your text to explicitly list the ways in which non-const refs are sometimes used, and which ones are OK (not many), and what they should be replaced with I think it would be more useful to readers of the style guide. Maybe you would disagree on some specific usage, but I don't see any specific disagreements in this thread (other than on how to express the guidance in english text). |
Since this rule is more lore then cannon (in that it is not written down nor linted for), and since C++11 is a modernization of the language. I see this as an excellent chance to improve our rules set. Or more correctly not canonize an outdated rule. |
Even V8 deviate from this rule (listing points just from the V8 public API): Lines 3398 to 3399 in c1b9be5
|
… in C++ style guide
… which is why they explicitly mention this in the doc comment. |
Lite CI (Doc only change): https://ci.nodejs.org/job/node-test-pull-request-lite-pipeline/1285/ |
Landed in 5550510 |
We generally avoid using non-const references if not necessary. This formalizes this rules by writing them down in the C++ style guide. (Note: Some reviews are from the original PR.) Refs: #23028 PR-URL: #23155 Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Ali Ijaz Sheikh <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]>
We generally avoid using non-const references if not necessary. This formalizes this rules by writing them down in the C++ style guide. (Note: Some reviews are from the original PR.) Refs: #23028 PR-URL: #23155 Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Ali Ijaz Sheikh <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]>
We generally avoid using non-const references if not necessary. This formalizes this rules by writing them down in the C++ style guide. (Note: Some reviews are from the original PR.) Refs: #23028 PR-URL: #23155 Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Ali Ijaz Sheikh <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]>
We generally avoid using non-const references if not necessary. This formalizes this rules by writing them down in the C++ style guide. (Note: Some reviews are from the original PR.) Refs: #23028 PR-URL: #23155 Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Ali Ijaz Sheikh <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]>
We generally avoid using non-const references if not necessary. This formalizes this rules by writing them down in the C++ style guide. (Note: Some reviews are from the original PR.) Refs: #23028 PR-URL: #23155 Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Ali Ijaz Sheikh <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]>
Split out from #23028 by request from @refack. I’ve added people who approved the other PR as reviewers on the commit. I’m also moving the
tsc-agenda
label over here, assuming @refack’s objection stands.We generally avoid using non-const references if not necessary. This
formalizes this rules by writing them down in the C++ style guide.
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes