mirror of
https://github.com/isocpp/CppCoreGuidelines.git
synced 2025-12-17 12:44:42 +03:00
many minor changes to the F section
This commit is contained in:
@@ -1793,7 +1793,7 @@ Function definition rules:
|
|||||||
* [F.2: A function should perform a single logical operation](#Rf-logical)
|
* [F.2: A function should perform a single logical operation](#Rf-logical)
|
||||||
* [F.3: Keep functions short and simple](#Rf-single)
|
* [F.3: Keep functions short and simple](#Rf-single)
|
||||||
* [F.4: If a function may have to be evaluated at compile time, declare it `constexpr`](#Rf-constexpr)
|
* [F.4: If a function may have to be evaluated at compile time, declare it `constexpr`](#Rf-constexpr)
|
||||||
* [F.5: If a function is very small and time critical, declare it inline](#Rf-inline)
|
* [F.5: If a function is very small and time-critical, declare it inline](#Rf-inline)
|
||||||
* [F.6: If your function may not throw, declare it `noexcept`](#Rf-noexcept)
|
* [F.6: If your function may not throw, declare it `noexcept`](#Rf-noexcept)
|
||||||
* [F.7: For general use, take `T*` or `T&` arguments rather than smart pointers](#Rf-smart)
|
* [F.7: For general use, take `T*` or `T&` arguments rather than smart pointers](#Rf-smart)
|
||||||
* [F.8: Prefer pure functions](#Rf-pure)
|
* [F.8: Prefer pure functions](#Rf-pure)
|
||||||
@@ -1801,12 +1801,13 @@ Function definition rules:
|
|||||||
Parameter passing expression rules:
|
Parameter passing expression rules:
|
||||||
|
|
||||||
* [F.15: Prefer simple and conventional ways of passing information](#Rf-conventional)
|
* [F.15: Prefer simple and conventional ways of passing information](#Rf-conventional)
|
||||||
* [F.16: For "in" parameters, pass cheaply copied types by value and others by reference to `const`](#Rf-in)
|
* [F.16: For "in" parameters, pass cheaply-copied types by value and others by reference to `const`](#Rf-in)
|
||||||
* [F.17: For "in-out" parameters, pass by reference to non-`const`](#Rf-inout)
|
* [F.17: For "in-out" parameters, pass by reference to non-`const`](#Rf-inout)
|
||||||
* [F.18: For "consume" parameters, pass by `X&&` and `std::move` the parameter](#Rf-consume)
|
* [F.18: For "consume" parameters, pass by `X&&` and `std::move` the parameter](#Rf-consume)
|
||||||
* [F.19: For "forward" parameters, pass by `TP&&` and only `std::forward` the parameter](#Rf-forward)
|
* [F.19: For "forward" parameters, pass by `TP&&` and only `std::forward` the parameter](#Rf-forward)
|
||||||
* [F.20: For "out" output values, prefer return values to output parameters](#Rf-out)
|
* [F.20: For "out" output values, prefer return values to output parameters](#Rf-out)
|
||||||
* [F.21: To return multiple "out" values, prefer returning a tuple or struct](#Rf-out-multi)
|
* [F.21: To return multiple "out" values, prefer returning a tuple or struct](#Rf-out-multi)
|
||||||
|
* [F.60: Prefer `T*` over `T&` when "no argument" is a valid option](#Rf-ptr-ref)
|
||||||
|
|
||||||
Parameter passing semantic rules:
|
Parameter passing semantic rules:
|
||||||
|
|
||||||
@@ -1821,7 +1822,7 @@ Value return semantic rules:
|
|||||||
|
|
||||||
* [F.42: Return a `T*` to indicate a position (only)](#Rf-return-ptr)
|
* [F.42: Return a `T*` to indicate a position (only)](#Rf-return-ptr)
|
||||||
* [F.43: Never (directly or indirectly) return a pointer to a local object](#Rf-dangle)
|
* [F.43: Never (directly or indirectly) return a pointer to a local object](#Rf-dangle)
|
||||||
* [F.44: Return a `T&` when "returning no object" isn't an option](#Rf-return-ref)
|
* [F.44: Return a `T&` when copy is undesirable and "returning no object" isn't an option](#Rf-return-ref)
|
||||||
* [F.45: Don't return a `T&&`](#Rf-return-ref-ref)
|
* [F.45: Don't return a `T&&`](#Rf-return-ref-ref)
|
||||||
* [F.46: `int` is the return type for `main()`](#Rf-main)
|
* [F.46: `int` is the return type for `main()`](#Rf-main)
|
||||||
* [F.47: Return `T&` from assignment operators.](#Rf-assignment-op)
|
* [F.47: Return `T&` from assignment operators.](#Rf-assignment-op)
|
||||||
@@ -1932,7 +1933,7 @@ These can now be combined where needed:
|
|||||||
print(cout, x);
|
print(cout, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
If there was a need, we could further templatize `read()` and `print()` on the data type, the I/O mechanism, etc. Example:
|
If there was a need, we could further templatize `read()` and `print()` on the data type, the I/O mechanism, the response to errors, etc. Example:
|
||||||
|
|
||||||
auto read = [](auto& input, auto& value) // better
|
auto read = [](auto& input, auto& value) // better
|
||||||
{
|
{
|
||||||
@@ -2097,7 +2098,7 @@ API will have to be refactored or drop `constexpr`.
|
|||||||
Impossible and unnecessary.
|
Impossible and unnecessary.
|
||||||
The compiler gives an error if a non-`constexpr` function is called where a constant is required.
|
The compiler gives an error if a non-`constexpr` function is called where a constant is required.
|
||||||
|
|
||||||
### <a name="Rf-inline"></a>F.5: If a function is very small and time critical, declare it `inline`
|
### <a name="Rf-inline"></a>F.5: If a function is very small and time-critical, declare it `inline`
|
||||||
|
|
||||||
##### Reason
|
##### Reason
|
||||||
|
|
||||||
@@ -2106,6 +2107,10 @@ Measure! Over the last 40 years or so, we have been promised compilers that can
|
|||||||
We are still waiting.
|
We are still waiting.
|
||||||
Specifying `inline` encourages the compiler to do a better job.
|
Specifying `inline` encourages the compiler to do a better job.
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
???
|
||||||
|
|
||||||
**Exception**: Do not put an `inline` function in what is meant to be a stable interface unless you are really sure that it will not change.
|
**Exception**: Do not put an `inline` function in what is meant to be a stable interface unless you are really sure that it will not change.
|
||||||
An inline function is part of the ABI.
|
An inline function is part of the ABI.
|
||||||
|
|
||||||
@@ -2201,15 +2206,19 @@ Passing a shared smart pointer (e.g., `std::shared_ptr`) implies a run-time cost
|
|||||||
|
|
||||||
void h(const unique_ptr<int>&); // doesn’t change ownership, but requires a particular ownership of the caller.
|
void h(const unique_ptr<int>&); // doesn’t change ownership, but requires a particular ownership of the caller.
|
||||||
|
|
||||||
|
void h(int&); // accepts any int
|
||||||
|
|
||||||
##### Note
|
##### Note
|
||||||
|
|
||||||
We can catch dangling pointers statically, so we don't need to rely on resource management to avoid violations from dangling pointers.
|
We can catch dangling pointers statically, so we don't need to rely on resource management to avoid violations from dangling pointers.
|
||||||
|
|
||||||
|
**See also**: [when to prefer `T*` and when to prefer `T&`](#Rf-ptr-ref).
|
||||||
|
|
||||||
**See also**: Discussion of [smart pointer use](#Rr-summary-smartptrs).
|
**See also**: Discussion of [smart pointer use](#Rr-summary-smartptrs).
|
||||||
|
|
||||||
##### Enforcement
|
##### Enforcement
|
||||||
|
|
||||||
Flag smart pointer arguments.
|
* ??? Difficult: Flag smart pointer arguments where ownership isn't affected.
|
||||||
|
|
||||||
### <a name="Rf-pure"></a>F.8: Prefer pure functions
|
### <a name="Rf-pure"></a>F.8: Prefer pure functions
|
||||||
|
|
||||||
@@ -2250,7 +2259,7 @@ The following tables summarize the advice in the following Guidelines, F.16-21.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### <a name="Rf-in"></a>F.16: For "in" parameters, pass cheaply copied types by value and others by reference to `const`
|
### <a name="Rf-in"></a>F.16: For "in" parameters, pass cheaply-copied types by value and others by reference to `const`
|
||||||
|
|
||||||
##### Reason
|
##### Reason
|
||||||
|
|
||||||
@@ -2272,7 +2281,8 @@ When copying is cheap, nothing beats the simplicity and safety of copying, and f
|
|||||||
For advanced uses (only), where you really need to optimize for rvalues passed to "input-only" parameters:
|
For advanced uses (only), where you really need to optimize for rvalues passed to "input-only" parameters:
|
||||||
|
|
||||||
* If the function is going to unconditionally move from the argument, take it by `&&`. See [F.18](#Rf-consume).
|
* If the function is going to unconditionally move from the argument, take it by `&&`. See [F.18](#Rf-consume).
|
||||||
* If the function is going to keep a copy of the argument, in addition to passing by `const&` add an overload that passes the parameter by `&&` and in the body `std::move`s it to its destination. Essentially this overloads a "consume"; see [F.18](#Rf-consume).
|
* If the function is going to keep a copy of the argument, in addition to passing by `const&` (for lvalues),
|
||||||
|
add an overload that passes the parameter by `&&` (for rvalues) and in the body `std::move`s it to its destination. Essentially this overloads a "consume"; see [F.18](#Rf-consume).
|
||||||
* In special cases, such as multiple "input + copy" parameters, consider using perfect forwarding. See [F.19](#Rf-forward).
|
* In special cases, such as multiple "input + copy" parameters, consider using perfect forwarding. See [F.19](#Rf-forward).
|
||||||
|
|
||||||
##### Example
|
##### Example
|
||||||
@@ -2285,12 +2295,13 @@ For advanced uses (only), where you really need to optimize for rvalues passed t
|
|||||||
|
|
||||||
Avoid "esoteric techniques" such as:
|
Avoid "esoteric techniques" such as:
|
||||||
|
|
||||||
* Passing arguments as `T&&` "for efficiency". Most rumors about performance advantages from passing by `&&` are false or brittle (but see [F.25](#Rf-pass-ref-move).)
|
* Passing arguments as `T&&` "for efficiency".
|
||||||
|
Most rumors about performance advantages from passing by `&&` are false or brittle (but see [F.25](#Rf-pass-ref-move).)
|
||||||
* Returning `const T&` from assignments and similar operations (see [F.47](#Rf-assignment-op).)
|
* Returning `const T&` from assignments and similar operations (see [F.47](#Rf-assignment-op).)
|
||||||
|
|
||||||
##### Example
|
##### Example
|
||||||
|
|
||||||
Assuming that `Matrix` has move operations (possibly by keeping its elements in a `std::vector`.
|
Assuming that `Matrix` has move operations (possibly by keeping its elements in a `std::vector`):
|
||||||
|
|
||||||
Matrix operator+(const Matrix& a, const Matrix& b)
|
Matrix operator+(const Matrix& a, const Matrix& b)
|
||||||
{
|
{
|
||||||
@@ -2305,7 +2316,7 @@ Assuming that `Matrix` has move operations (possibly by keeping its elements in
|
|||||||
|
|
||||||
##### Notes
|
##### Notes
|
||||||
|
|
||||||
The return value optimization doesn't handle the assignment case.
|
The return value optimization doesn't handle the assignment case, but the move assignment does.
|
||||||
|
|
||||||
A reference may be assumed to refer to a valid object (language rule).
|
A reference may be assumed to refer to a valid object (language rule).
|
||||||
There is no (legitimate) "null reference."
|
There is no (legitimate) "null reference."
|
||||||
@@ -2359,6 +2370,10 @@ If the writer of `g()` makes an assumption about the size of `buffer` a bad logi
|
|||||||
|
|
||||||
It's efficient and eliminates bugs at the call site: `X&&` binds to rvalues, which requires an explicit `std::move` at the call site if passing an lvalue.
|
It's efficient and eliminates bugs at the call site: `X&&` binds to rvalues, which requires an explicit `std::move` at the call site if passing an lvalue.
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
???
|
||||||
|
|
||||||
##### Exception
|
##### Exception
|
||||||
|
|
||||||
Unique owner types that are move-only and cheap-to-move, such as `unique_ptr`, can also be passed by value which is simpler to write and achieves the same effect. Passing by value does generate one extra (cheap) move operation, but prefer simplicity and clarity first.
|
Unique owner types that are move-only and cheap-to-move, such as `unique_ptr`, can also be passed by value which is simpler to write and achieves the same effect. Passing by value does generate one extra (cheap) move operation, but prefer simplicity and clarity first.
|
||||||
@@ -2384,6 +2399,7 @@ In that case, and only that case, make the parameter `TP&&` where `TP` is a temp
|
|||||||
return f(forward<Args>(args)...);
|
return f(forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
??? calls ???
|
||||||
|
|
||||||
##### Enforcement
|
##### Enforcement
|
||||||
* Flag a function that takes a `TP&&` parameter (where `TP` is a template type parameter name) and does anything with it other than `std::forward`ing it exactly once on every static path.
|
* Flag a function that takes a `TP&&` parameter (where `TP` is a template type parameter name) and does anything with it other than `std::forward`ing it exactly once on every static path.
|
||||||
@@ -2411,6 +2427,8 @@ A struct of many (individually cheap-to-move) elements may be in aggregate expen
|
|||||||
|
|
||||||
It is not recommended to return a `const` value. Such older advice is now obsolete; it does not add value, and it interferes with move semantics.
|
It is not recommended to return a `const` value. Such older advice is now obsolete; it does not add value, and it interferes with move semantics.
|
||||||
|
|
||||||
|
??? example ???
|
||||||
|
|
||||||
##### Exceptions
|
##### Exceptions
|
||||||
|
|
||||||
* For non-value types, such as types in an inheritance hierarchy, return the object by `unique_ptr` or `shared_ptr`.
|
* For non-value types, such as types in an inheritance hierarchy, return the object by `unique_ptr` or `shared_ptr`.
|
||||||
@@ -2477,7 +2495,11 @@ With C++17 we may be able to write something like this, also declaring the varia
|
|||||||
auto { iter, success } = myset.insert("Hello");
|
auto { iter, success } = myset.insert("Hello");
|
||||||
if (success) do_something_with(iter);
|
if (success) do_something_with(iter);
|
||||||
|
|
||||||
**Exception**: For types like `string` and `vector` that carry additional capacity, it can sometimes be useful to treat it as in/out instead by using the "caller-allocated out" pattern, which is to pass an output-only object by reference to non-`const` so that when the callee writes to it the object can reuse any capacity or other resources that it already contains. This technique can dramatically reduce the number of allocations in a loop that repeatedly calls other functions to get string values, by using a single string object for the entire loop.
|
**Exception**: For types like `string` and `vector` that carry additional capacity, it can sometimes be useful to treat it as in/out instead by using the "caller-allocated out" pattern,
|
||||||
|
which is to pass an output-only object by reference to non-`const` so that when the callee writes to it the object can reuse any capacity or other resources that it already contains.
|
||||||
|
This technique can dramatically reduce the number of allocations in a loop that repeatedly calls other functions to get string values, by using a single string object for the entire loop.
|
||||||
|
|
||||||
|
??? example ???
|
||||||
|
|
||||||
##### Note
|
##### Note
|
||||||
|
|
||||||
@@ -2502,28 +2524,18 @@ In traditional C and C++ code, plain `T*` is used for many weakly-related purpos
|
|||||||
* Identify an array with a length specified separately
|
* Identify an array with a length specified separately
|
||||||
* Identify a location in an array
|
* Identify a location in an array
|
||||||
|
|
||||||
For example, `not_null<T*>` makes it obvious to a reader (human or machine) that a test for `nullptr` is not necessary before dereference.
|
|
||||||
Additionally, when debugging, `owner<T*>` and `not_null<T>` can be instrumented to check for correctness.
|
|
||||||
|
|
||||||
##### Example
|
##### Example
|
||||||
|
|
||||||
Consider:
|
void use(int* p, char* s, int* q)
|
||||||
|
{
|
||||||
int length(Record* p);
|
*++p = 666; // Bad: we don't know if p points to two elements; assume it does not or use span<int>
|
||||||
|
cout << s; // Bad: we don't know if that s points to a zero-terminated array of char; assume it does not or use zstring
|
||||||
When I call `length(p)` should I test for `p == nullptr` first? Should the implementation of `length()` test for `p == nullptr`?
|
delete q; // Bad: we don't know if *q is allocated on the free store; assume it does not or use owner
|
||||||
|
}
|
||||||
int length(not_null<Record*> p); // it is the caller's job to make sure p != nullptr
|
|
||||||
|
|
||||||
int length(Record* p); // the implementor of length() must assume that p == nullptr is possible
|
|
||||||
|
|
||||||
##### Note
|
##### Note
|
||||||
|
|
||||||
A `not_null<T*>` is assumed not to be the `nullptr`; a `T*` may be the `nullptr`; both can be represented in memory as a `T*` (so no run-time overhead is implied).
|
`owner<T*>` represents ownership, `zstring` represents a C-style string.
|
||||||
|
|
||||||
##### Note
|
|
||||||
|
|
||||||
`owner<T*>` represents ownership.
|
|
||||||
|
|
||||||
**Also**: Assume that a `T*` obtained from a smart pointer to `T` (e.g., `unique_ptr<T>`) points to a single element.
|
**Also**: Assume that a `T*` obtained from a smart pointer to `T` (e.g., `unique_ptr<T>`) points to a single element.
|
||||||
|
|
||||||
@@ -2541,18 +2553,26 @@ Clarity. A function with a `not_null<T>` parameter makes it clear that the calle
|
|||||||
|
|
||||||
##### Example
|
##### Example
|
||||||
|
|
||||||
not_null<T*> check(T* p) { if (p) return not_null<T*>{p}; throw Unexpected_nullptr{}; }
|
`not_null<T*>` makes it obvious to a reader (human or machine) that a test for `nullptr` is not necessary before dereference.
|
||||||
|
Additionally, when debugging, `owner<T*>` and `not_null<T>` can be instrumented to check for correctness.
|
||||||
|
|
||||||
void computer(not_null<span<int>> p)
|
Consider:
|
||||||
{
|
|
||||||
if (0 < p.size()) { // bad: redundant test
|
int length(Record* p);
|
||||||
// ...
|
|
||||||
}
|
When I call `length(p)` should I test for `p == nullptr` first? Should the implementation of `length()` test for `p == nullptr`?
|
||||||
}
|
|
||||||
|
int length(not_null<Record*> p); // it is the caller's job to make sure p != nullptr
|
||||||
|
|
||||||
|
int length(Record* p); // the implementor of length() must assume that p == nullptr is possible
|
||||||
|
|
||||||
##### Note
|
##### Note
|
||||||
|
|
||||||
`not_null` is not just for built-in pointers. It works for `span`, `string_span`, `unique_ptr`, `shared_ptr`, and other pointer-like types.
|
A `not_null<T*>` is assumed not to be the `nullptr`; a `T*` may be the `nullptr`; both can be represented in memory as a `T*` (so no run-time overhead is implied).
|
||||||
|
|
||||||
|
##### Note
|
||||||
|
|
||||||
|
`not_null` is not just for built-in pointers. It works for `unique_ptr`, `shared_ptr`, and other pointer-like types.
|
||||||
|
|
||||||
##### Enforcement
|
##### Enforcement
|
||||||
|
|
||||||
@@ -2576,14 +2596,27 @@ Informal/non-explicit ranges are a source of errors.
|
|||||||
|
|
||||||
##### Note
|
##### Note
|
||||||
|
|
||||||
Ranges are extremely common in C++ code. Typically, they are implicit and their correct use is very hard to ensure. In particular, given a pair of arguments `(p, n)` designating an array [`p`:`p+n`), it is in general impossible to know if there really are `n` elements to access following `*p`. `span<T>` and `span_p<T>` are simple helper classes designating a [`p`:`q`) range and a range starting with `p` and ending with the first element for which a predicate is true, respectively.
|
Ranges are extremely common in C++ code. Typically, they are implicit and their correct use is very hard to ensure.
|
||||||
|
In particular, given a pair of arguments `(p, n)` designating an array [`p`:`p+n`),
|
||||||
|
it is in general impossible to know if there really are `n` elements to access following `*p`.
|
||||||
|
`span<T>` and `span_p<T>` are simple helper classes designating a [`p`:`q`) range and a range starting with `p` and ending with the first element for which a predicate is true, respectively.
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
A `span` represents a range of elements, but how do we manipulate elements of that range?
|
||||||
|
|
||||||
|
void f(span<int> s)
|
||||||
|
{
|
||||||
|
for (int x : s) cout << x << '\n'; // range traversal (guaranteed correct)
|
||||||
|
for (int i = 0; i<s.size(); ++i) cout << x << '\n'; // C-style traversal (potentially checked)
|
||||||
|
s[7] = 9; // random access (potentially checked)
|
||||||
|
std::sort(&s[0],&s[s.size()/2); // extract pointers (potentially checked)
|
||||||
|
}
|
||||||
|
|
||||||
##### Note
|
##### Note
|
||||||
|
|
||||||
A `span<T>` object does not own its elements and is so small that it can be passed by value.
|
A `span<T>` object does not own its elements and is so small that it can be passed by value.
|
||||||
|
|
||||||
##### Note
|
|
||||||
|
|
||||||
Passing a `span` object as an argument is exactly as efficient as passing a pair of pointer arguments or passing a pointer and an integer count.
|
Passing a `span` object as an argument is exactly as efficient as passing a pair of pointer arguments or passing a pointer and an integer count.
|
||||||
|
|
||||||
**See also**: [Support library](#S-gsl).
|
**See also**: [Support library](#S-gsl).
|
||||||
@@ -2671,13 +2704,48 @@ Prefer a `unique_ptr` over a `shared_ptr` if there is never more than one owner
|
|||||||
|
|
||||||
Note that pervasive use of `shared_ptr` has a cost (atomic operations on the `shared_ptr`'s reference count have a measurable aggregate cost).
|
Note that pervasive use of `shared_ptr` has a cost (atomic operations on the `shared_ptr`'s reference count have a measurable aggregate cost).
|
||||||
|
|
||||||
**Alternative**: Have a single object own the shared object (e.g. a scoped object) and destroy that (preferably implicitly) when all users have completed.
|
##### Alternative
|
||||||
|
|
||||||
|
Have a single object own the shared object (e.g. a scoped object) and destroy that (preferably implicitly) when all users have completed.
|
||||||
|
|
||||||
##### Enforcement
|
##### Enforcement
|
||||||
|
|
||||||
(Not enforceable) This is a too complex pattern to reliably detect.
|
(Not enforceable) This is a too complex pattern to reliably detect.
|
||||||
|
|
||||||
|
|
||||||
|
### <a name="Rf-ptr-ref"></a>F.60: Prefer `T*` over `T&` when "no argument" is a valid option
|
||||||
|
|
||||||
|
##### Reason
|
||||||
|
|
||||||
|
A pointer (`T*`) can be a `nullptr` and a reference (`T&`) cannot, there is no valid "null reference".
|
||||||
|
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.
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
string zstring_to_string(zstring p) // zstring is a char*; that is a C-style string
|
||||||
|
{
|
||||||
|
if (p==nullptr) return string{}; // p might be nullptr; remember to check
|
||||||
|
return string{p};
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(const vector<int>& r)
|
||||||
|
{
|
||||||
|
// r refers to a vector<int>; no check needed
|
||||||
|
}
|
||||||
|
|
||||||
|
##### Note
|
||||||
|
|
||||||
|
It is possible, but not valid C++ to construct a reference that is essentially a `nullptr` (e.g., `T* p = nullptr; T& r = (T&)*p;`).
|
||||||
|
That error is very uncommon.
|
||||||
|
|
||||||
|
##### Note
|
||||||
|
|
||||||
|
If you prefer the pointer notation (`->` and/or `*` vs. `.`), `not_null<T*>` provides the same guarantee as `T&`.
|
||||||
|
|
||||||
|
##### Enforcement
|
||||||
|
|
||||||
|
* Flag ???
|
||||||
|
|
||||||
### <a name="Rf-return-ptr"></a>F.42: Return a `T*` to indicate a position (only)
|
### <a name="Rf-return-ptr"></a>F.42: Return a `T*` to indicate a position (only)
|
||||||
|
|
||||||
##### Reason
|
##### Reason
|
||||||
@@ -2701,6 +2769,7 @@ Importantly, that does not imply a transfer of ownership of the pointed-to objec
|
|||||||
##### Note
|
##### Note
|
||||||
|
|
||||||
Positions can also be transferred by iterators, indices, and references.
|
Positions can also be transferred by iterators, indices, and references.
|
||||||
|
A reference is often a superior alternative to a pointer [if there is no need to use `nullptr`](#Rf-ptr-ref) or [if the object referred to should not change](???).
|
||||||
|
|
||||||
##### Note
|
##### Note
|
||||||
|
|
||||||
@@ -2830,7 +2899,7 @@ It can be detected/prevented with similar techniques.
|
|||||||
|
|
||||||
Preventable through static analysis.
|
Preventable through static analysis.
|
||||||
|
|
||||||
### <a name="Rf-return-ref"></a>F.44: Return a `T&` when "returning no object" isn't needed
|
### <a name="Rf-return-ref"></a>F.44: Return a `T&` when copy is undesirable and "returning no object" isn't needed
|
||||||
|
|
||||||
##### Reason
|
##### Reason
|
||||||
|
|
||||||
@@ -2858,7 +2927,7 @@ The language guarantees that a `T&` refers to an object, so that testing for `nu
|
|||||||
|
|
||||||
##### Enforcement
|
##### Enforcement
|
||||||
|
|
||||||
???
|
Flag functions where no `return` expression could yield `nullptr`
|
||||||
|
|
||||||
### <a name="Rf-return-ref-ref"></a>F.45: Don't return a `T&&`
|
### <a name="Rf-return-ref-ref"></a>F.45: Don't return a `T&&`
|
||||||
|
|
||||||
@@ -2912,6 +2981,10 @@ Declaring `main` (the one global `main` of a program) `void` limits portability.
|
|||||||
std::cout << "This is the way to do it\n";
|
std::cout << "This is the way to do it\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
##### Note
|
||||||
|
|
||||||
|
We mention this only because of the persistence of this error in the community.
|
||||||
|
|
||||||
##### Enforcement
|
##### Enforcement
|
||||||
|
|
||||||
* The compiler should do it
|
* The compiler should do it
|
||||||
@@ -2928,9 +3001,8 @@ principle of "do as the ints do."
|
|||||||
|
|
||||||
##### Note
|
##### Note
|
||||||
|
|
||||||
Historically there was some guidance to make the assignment operator return
|
Historically there was some guidance to make the assignment operator return `const T&`.
|
||||||
`const T&`. This was primarily to avoid code of the form `(a=b)=c` - such code
|
This was primarily to avoid code of the form `(a=b)=c` - such code is not common enough to warrant violating consistency with standard types.
|
||||||
is not common enough to warrant violating consistency with standard types.
|
|
||||||
|
|
||||||
##### Example
|
##### Example
|
||||||
|
|
||||||
@@ -9417,7 +9489,7 @@ Performance rule summary:
|
|||||||
* [PER.14: Minimize the number of allocations and deallocations](#Rper-alloc)
|
* [PER.14: Minimize the number of allocations and deallocations](#Rper-alloc)
|
||||||
* [PER.15: Do not allocate on a critical branch](#Rper-alloc0)
|
* [PER.15: Do not allocate on a critical branch](#Rper-alloc0)
|
||||||
* [PER.16: Use compact data structures](#Rper-compact)
|
* [PER.16: Use compact data structures](#Rper-compact)
|
||||||
* [PER.17: Declare the most used member of a time critical struct first](#Rper-struct)
|
* [PER.17: Declare the most used member of a time-critical struct first](#Rper-struct)
|
||||||
* [PER.18: Space is time](#Rper-space)
|
* [PER.18: Space is time](#Rper-space)
|
||||||
* [PER.19: Access memory predictably](#Rper-access)
|
* [PER.19: Access memory predictably](#Rper-access)
|
||||||
* [PER.30: Avoid context switches on the critical path](#Rper-context)
|
* [PER.30: Avoid context switches on the critical path](#Rper-context)
|
||||||
@@ -9554,7 +9626,7 @@ Performance is typically dominated by memory access times.
|
|||||||
|
|
||||||
???
|
???
|
||||||
|
|
||||||
### <a name="Rper-struct"></a>PER.17: Declare the most used member of a time critical struct first
|
### <a name="Rper-struct"></a>PER.17: Declare the most used member of a time-critical struct first
|
||||||
|
|
||||||
???
|
???
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user