|
1 | 1 | # <a name="main"></a>C++ Core Guidelines
|
2 | 2 |
|
3 |
| -January 3, 2022 |
| 3 | +July 13, 2022 |
4 | 4 |
|
5 | 5 |
|
6 | 6 | Editors:
|
@@ -52,7 +52,7 @@ Supporting sections:
|
52 | 52 | * [RF: References](#S-references)
|
53 | 53 | * [Pro: Profiles](#S-profile)
|
54 | 54 | * [GSL: Guidelines support library](#S-gsl)
|
55 |
| -* [NL: Naming and layout rules](#S-naming) |
| 55 | +* [NL: Naming and layout suggestions](#S-naming) |
56 | 56 | * [FAQ: Answers to frequently asked questions](#S-faq)
|
57 | 57 | * [Appendix A: Libraries](#S-libraries)
|
58 | 58 | * [Appendix B: Modernizing code](#S-modernizing)
|
@@ -445,7 +445,7 @@ Supporting sections:
|
445 | 445 | * [RF: References](#S-references)
|
446 | 446 | * [Pro: Profiles](#S-profile)
|
447 | 447 | * [GSL: Guidelines support library](#S-gsl)
|
448 |
| -* [NL: Naming and layout rules](#S-naming) |
| 448 | +* [NL: Naming and layout suggestions](#S-naming) |
449 | 449 | * [FAQ: Answers to frequently asked questions](#S-faq)
|
450 | 450 | * [Appendix A: Libraries](#S-libraries)
|
451 | 451 | * [Appendix B: Modernizing code](#S-modernizing)
|
@@ -2432,7 +2432,6 @@ Naming that lambda breaks up the expression into its logical parts and provides
|
2432 | 2432 | auto lessT = [](T x, T y) { return x.rank() < y.rank() && x.value() < y.value(); };
|
2433 | 2433 |
|
2434 | 2434 | sort(a, b, lessT);
|
2435 |
| - find_if(a, b, lessT); |
2436 | 2435 |
|
2437 | 2436 | The shortest code is not always the best for performance or maintainability.
|
2438 | 2437 |
|
@@ -2927,31 +2926,11 @@ For advanced uses (only), where you really need to optimize for rvalues passed t
|
2927 | 2926 |
|
2928 | 2927 | void sink(unique_ptr<widget>); // input only, and moves ownership of the widget
|
2929 | 2928 |
|
2930 |
| -Avoid "esoteric techniques" such as: |
2931 |
| - |
2932 |
| -* Passing arguments as `T&&` "for efficiency". |
2933 |
| - Most rumors about performance advantages from passing by `&&` are false or brittle (but see [F.18](#Rf-consume) and [F.19](#Rf-forward)). |
2934 |
| -* Returning `const T&` from assignments and similar operations (see [F.47](#Rf-assignment-op).) |
2935 |
| - |
2936 |
| -##### Example |
2937 |
| - |
2938 |
| -Assuming that `Matrix` has move operations (possibly by keeping its elements in a `std::vector`): |
2939 |
| - |
2940 |
| - Matrix operator+(const Matrix& a, const Matrix& b) |
2941 |
| - { |
2942 |
| - Matrix res; |
2943 |
| - // ... fill res with the sum ... |
2944 |
| - return res; |
2945 |
| - } |
2946 |
| - |
2947 |
| - Matrix x = m1 + m2; // move constructor |
2948 |
| - |
2949 |
| - y = m3 + m3; // move assignment |
| 2929 | +Avoid "esoteric techniques" such as passing arguments as `T&&` "for efficiency". |
| 2930 | +Most rumors about performance advantages from passing by `&&` are false or brittle (but see [F.18](#Rf-consume) and [F.19](#Rf-forward)). |
2950 | 2931 |
|
2951 | 2932 | ##### Notes
|
2952 | 2933 |
|
2953 |
| -The return value optimization doesn't handle the assignment case, but the move assignment does. |
2954 |
| - |
2955 | 2934 | A reference can be assumed to refer to a valid object (language rule).
|
2956 | 2935 | There is no (legitimate) "null reference."
|
2957 | 2936 | If you need the notion of an optional value, use a pointer, `std::optional`, or a special value used to denote "no value."
|
@@ -3104,6 +3083,26 @@ The argument against is that it prevents (very frequent) use of move semantics.
|
3104 | 3083 | * If a type is expensive to move (e.g., `array<BigPOD>`), consider allocating it on the free store and return a handle (e.g., `unique_ptr`), or passing it in a reference to non-`const` target object to fill (to be used as an out-parameter).
|
3105 | 3084 | * To reuse an object that carries capacity (e.g., `std::string`, `std::vector`) across multiple calls to the function in an inner loop: [treat it as an in/out parameter and pass by reference](#Rf-out-multi).
|
3106 | 3085 |
|
| 3086 | +##### Example |
| 3087 | + |
| 3088 | +Assuming that `Matrix` has move operations (possibly by keeping its elements in a `std::vector`): |
| 3089 | + |
| 3090 | + Matrix operator+(const Matrix& a, const Matrix& b) |
| 3091 | + { |
| 3092 | + Matrix res; |
| 3093 | + // ... fill res with the sum ... |
| 3094 | + return res; |
| 3095 | + } |
| 3096 | + |
| 3097 | + Matrix x = m1 + m2; // move constructor |
| 3098 | + |
| 3099 | + y = m3 + m3; // move assignment |
| 3100 | + |
| 3101 | + |
| 3102 | +##### Note |
| 3103 | + |
| 3104 | +The return value optimization doesn't handle the assignment case, but the move assignment does. |
| 3105 | + |
3107 | 3106 | ##### Example
|
3108 | 3107 |
|
3109 | 3108 | struct Package { // exceptional case: expensive-to-move object
|
@@ -3230,6 +3229,39 @@ Another example, use a specific type along the lines of `variant<T, error_code>`
|
3230 | 3229 | * Output parameters should be replaced by return values.
|
3231 | 3230 | An output parameter is one that the function writes to, invokes a non-`const` member function, or passes on as a non-`const`.
|
3232 | 3231 |
|
| 3232 | +### <a name="Rf-ptr-ref"></a>F.60: Prefer `T*` over `T&` when "no argument" is a valid option |
| 3233 | + |
| 3234 | +##### Reason |
| 3235 | + |
| 3236 | +A pointer (`T*`) can be a `nullptr` and a reference (`T&`) cannot, there is no valid "null reference". |
| 3237 | +Sometimes having `nullptr` as an alternative to indicated "no object" is useful, but if it is not, a reference is notationally simpler and might yield better code. |
| 3238 | + |
| 3239 | +##### Example |
| 3240 | + |
| 3241 | + string zstring_to_string(zstring p) // zstring is a char*; that is a C-style string |
| 3242 | + { |
| 3243 | + if (!p) return string{}; // p might be nullptr; remember to check |
| 3244 | + return string{p}; |
| 3245 | + } |
| 3246 | + |
| 3247 | + void print(const vector<int>& r) |
| 3248 | + { |
| 3249 | + // r refers to a vector<int>; no check needed |
| 3250 | + } |
| 3251 | + |
| 3252 | +##### Note |
| 3253 | + |
| 3254 | +It is possible, but not valid C++ to construct a reference that is essentially a `nullptr` (e.g., `T* p = nullptr; T& r = *p;`). |
| 3255 | +That error is very uncommon. |
| 3256 | + |
| 3257 | +##### Note |
| 3258 | + |
| 3259 | +If you prefer the pointer notation (`->` and/or `*` vs. `.`), `not_null<T*>` provides the same guarantee as `T&`. |
| 3260 | + |
| 3261 | +##### Enforcement |
| 3262 | + |
| 3263 | +* Flag ??? |
| 3264 | + |
3233 | 3265 | ### <a name="Rf-ptr"></a>F.22: Use `T*` or `owner<T*>` to designate a single object
|
3234 | 3266 |
|
3235 | 3267 | ##### Reason
|
@@ -3468,39 +3500,6 @@ Have a single object own the shared object (e.g. a scoped object) and destroy th
|
3468 | 3500 |
|
3469 | 3501 | (Not enforceable) This is a too complex pattern to reliably detect.
|
3470 | 3502 |
|
3471 |
| -### <a name="Rf-ptr-ref"></a>F.60: Prefer `T*` over `T&` when "no argument" is a valid option |
3472 |
| - |
3473 |
| -##### Reason |
3474 |
| - |
3475 |
| -A pointer (`T*`) can be a `nullptr` and a reference (`T&`) cannot, there is no valid "null reference". |
3476 |
| -Sometimes having `nullptr` as an alternative to indicated "no object" is useful, but if it is not, a reference is notationally simpler and might yield better code. |
3477 |
| - |
3478 |
| -##### Example |
3479 |
| - |
3480 |
| - string zstring_to_string(zstring p) // zstring is a char*; that is a C-style string |
3481 |
| - { |
3482 |
| - if (!p) return string{}; // p might be nullptr; remember to check |
3483 |
| - return string{p}; |
3484 |
| - } |
3485 |
| - |
3486 |
| - void print(const vector<int>& r) |
3487 |
| - { |
3488 |
| - // r refers to a vector<int>; no check needed |
3489 |
| - } |
3490 |
| - |
3491 |
| -##### Note |
3492 |
| - |
3493 |
| -It is possible, but not valid C++ to construct a reference that is essentially a `nullptr` (e.g., `T* p = nullptr; T& r = *p;`). |
3494 |
| -That error is very uncommon. |
3495 |
| - |
3496 |
| -##### Note |
3497 |
| - |
3498 |
| -If you prefer the pointer notation (`->` and/or `*` vs. `.`), `not_null<T*>` provides the same guarantee as `T&`. |
3499 |
| - |
3500 |
| -##### Enforcement |
3501 |
| - |
3502 |
| -* Flag ??? |
3503 |
| - |
3504 | 3503 | ### <a name="Rf-return-ptr"></a>F.42: Return a `T*` to indicate a position (only)
|
3505 | 3504 |
|
3506 | 3505 | ##### Reason
|
@@ -4477,7 +4476,7 @@ For example, a derived class might be allowed to skip a run-time check because i
|
4477 | 4476 |
|
4478 | 4477 | ##### Note
|
4479 | 4478 |
|
4480 |
| -Prefer the order `public` members before `protected` members before `private` members [see](#Rl-order). |
| 4479 | +Prefer the order `public` members before `protected` members before `private` members; see [NL.16](#Rl-order). |
4481 | 4480 |
|
4482 | 4481 | ##### Enforcement
|
4483 | 4482 |
|
@@ -4993,12 +4992,18 @@ There is a lot of code that is non-specific about ownership.
|
4993 | 4992 |
|
4994 | 4993 | ##### Example
|
4995 | 4994 |
|
4996 |
| - ??? |
| 4995 | + class legacy_class |
| 4996 | + { |
| 4997 | + foo* m_owning; // Bad: change to unique_ptr<T> or owner<T*> |
| 4998 | + bar* m_observer; // OK: keep |
| 4999 | + } |
| 5000 | + |
| 5001 | +The only way to determine ownership may be code analysis. |
4997 | 5002 |
|
4998 | 5003 | ##### Note
|
4999 | 5004 |
|
5000 |
| -If the `T*` or `T&` is owning, mark it `owning`. If the `T*` is not owning, consider marking it `ptr`. |
5001 |
| -This will aid documentation and analysis. |
| 5005 | +Ownership should be clear in new code (and refactored legacy code) according to [R.20](#Rr-owner) for owning |
| 5006 | +pointers and [R.3](#Rr-ptr) for non-owning pointers. References should never own [R.4](#Rr-ref). |
5002 | 5007 |
|
5003 | 5008 | ##### Enforcement
|
5004 | 5009 |
|
@@ -8434,7 +8439,7 @@ Many parts of the C++ semantics assume its default meaning.
|
8434 | 8439 |
|
8435 | 8440 | If you "mess with" operator `&` be sure that its definition has matching meanings for `->`, `[]`, `*`, and `.` on the result type.
|
8436 | 8441 | Note that operator `.` currently cannot be overloaded so a perfect system is impossible.
|
8437 |
| -We hope to remedy that: <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4477.pdf>. |
| 8442 | +We hope to remedy that: [Operator Dot (R2)](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4477.pdf). |
8438 | 8443 | Note that `std::addressof()` always yields a built-in pointer.
|
8439 | 8444 |
|
8440 | 8445 | ##### Enforcement
|
@@ -15623,7 +15628,7 @@ Error-handling rule summary:
|
15623 | 15628 | * [E.13: Never throw while being the direct owner of an object](#Re-never-throw)
|
15624 | 15629 | * [E.14: Use purpose-designed user-defined types as exceptions (not built-in types)](#Re-exception-types)
|
15625 | 15630 | * [E.15: Throw by value, catch exceptions from a hierarchy by reference](#Re-exception-ref)
|
15626 |
| -* [E.16: Destructors, deallocation, and `swap` must never fail](#Re-never-fail) |
| 15631 | +* [E.16: Destructors, deallocation, `swap`, and exception type copy/move construction must never fail](#Re-never-fail) |
15627 | 15632 | * [E.17: Don't try to catch every exception in every function](#Re-not-always)
|
15628 | 15633 | * [E.18: Minimize the use of explicit `try`/`catch`](#Re-catch)
|
15629 | 15634 | * [E.19: Use a `final_action` object to express cleanup if no suitable resource handle is available](#Re-finally)
|
@@ -16090,11 +16095,11 @@ To rethrow a caught exception use `throw;` not `throw e;`. Using `throw e;` woul
|
16090 | 16095 | * Flag catching by value of a type that has a virtual function.
|
16091 | 16096 | * Flag throwing raw pointers.
|
16092 | 16097 |
|
16093 |
| -### <a name="Re-never-fail"></a>E.16: Destructors, deallocation, and `swap` must never fail |
| 16098 | +### <a name="Re-never-fail"></a>E.16: Destructors, deallocation, `swap`, and exception type copy/move construction must never fail |
16094 | 16099 |
|
16095 | 16100 | ##### Reason
|
16096 | 16101 |
|
16097 |
| -We don't know how to write reliable programs if a destructor, a swap, or a memory deallocation fails; that is, if it exits by an exception or simply doesn't perform its required action. |
| 16102 | +We don't know how to write reliable programs if a destructor, a swap, a memory deallocation, or attempting to copy/move-construct an exception object fails; that is, if it exits by an exception or simply doesn't perform its required action. |
16098 | 16103 |
|
16099 | 16104 | ##### Example, don't
|
16100 | 16105 |
|
@@ -16123,14 +16128,17 @@ The standard library assumes that destructors, deallocation functions (e.g., `op
|
16123 | 16128 |
|
16124 | 16129 | ##### Note
|
16125 | 16130 |
|
16126 |
| -Deallocation functions, including `operator delete`, must be `noexcept`. `swap` functions must be `noexcept`. |
16127 |
| -Most destructors are implicitly `noexcept` by default. |
16128 |
| -Also, [make move operations `noexcept`](#Rc-move-noexcept). |
| 16131 | +* Deallocation functions, including `operator delete`, must be `noexcept`. |
| 16132 | +* `swap` functions must be `noexcept`. |
| 16133 | +* Most destructors are implicitly `noexcept` by default. |
| 16134 | +* Also, [make move operations `noexcept`](#Rc-move-noexcept). |
| 16135 | +* If writing a type intended to be used as an exception type, ensure its copy constructor is not `noexcept`. In general we cannot mechanically enforce this, because we do not know whether a type is intended to be used as an exception type. |
| 16136 | +* Try not to `throw` a type whose copy constructor is not `noexcept`. In general we cannot mechanically enforce this, because even `throw std::string(...)` could throw but does not in practice. |
16129 | 16137 |
|
16130 | 16138 | ##### Enforcement
|
16131 | 16139 |
|
16132 |
| -Catch destructors, deallocation operations, and `swap`s that `throw`. |
16133 |
| -Catch such operations that are not `noexcept`. |
| 16140 | +* Catch destructors, deallocation operations, and `swap`s that `throw`. |
| 16141 | +* Catch such operations that are not `noexcept`. |
16134 | 16142 |
|
16135 | 16143 | **See also**: [discussion](#Sd-never-fail)
|
16136 | 16144 |
|
@@ -16207,14 +16215,14 @@ Better:
|
16207 | 16215 |
|
16208 | 16216 | ##### Reason
|
16209 | 16217 |
|
16210 |
| -`finally` is less verbose and harder to get wrong than `try`/`catch`. |
| 16218 | +`finally` from the [GSL](#S-gsl) is less verbose and harder to get wrong than `try`/`catch`. |
16211 | 16219 |
|
16212 | 16220 | ##### Example
|
16213 | 16221 |
|
16214 | 16222 | void f(int n)
|
16215 | 16223 | {
|
16216 | 16224 | void* p = malloc(n);
|
16217 |
| - auto _ = finally([p] { free(p); }); |
| 16225 | + auto _ = gsl::finally([p] { free(p); }); |
16218 | 16226 | // ...
|
16219 | 16227 | }
|
16220 | 16228 |
|
@@ -20302,6 +20310,14 @@ For writing to a file, there is rarely a need to `flush`.
|
20302 | 20310 |
|
20303 | 20311 | ##### Note
|
20304 | 20312 |
|
| 20313 | +For string streams (specifically `ostringstream`), the insertion of an `endl` is entirely equivalent |
| 20314 | +to the insertion of a `'\n'` character, but also in this case, `endl` might be significantly slower. |
| 20315 | + |
| 20316 | +`endl` does *not* take care of producing a platform specific end-of-line sequence (like "\r\n" on |
| 20317 | +Windows). So for a string stream, `s << endl` just inserts a *single* character, `'\n'`. |
| 20318 | + |
| 20319 | +##### Note |
| 20320 | + |
20305 | 20321 | Apart from the (occasionally important) issue of performance,
|
20306 | 20322 | the choice between `'\n'` and `endl` is almost completely aesthetic.
|
20307 | 20323 |
|
@@ -21232,7 +21248,7 @@ Many of them are very similar to what became part of the ISO C++ standard in C++
|
21232 | 21248 | * `Unique_pointer` // A type that matches `Pointer`, is movable, and is not copyable
|
21233 | 21249 | * `Shared_pointer` // A type that matches `Pointer`, and is copyable
|
21234 | 21250 |
|
21235 |
| -# <a name="S-naming"></a>NL: Naming and layout rules |
| 21251 | +# <a name="S-naming"></a>NL: Naming and layout suggestions |
21236 | 21252 |
|
21237 | 21253 | Consistent naming and layout are helpful.
|
21238 | 21254 | If for no other reason because it minimizes "my style is better than your style" arguments.
|
|
0 commit comments