mirror of
https://github.com/isocpp/CppCoreGuidelines.git
synced 2025-12-18 21:24:41 +03:00
update
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# <a name="main"></a>C++ Core Guidelines
|
||||
|
||||
January 3, 2019
|
||||
March 7, 2019
|
||||
|
||||
|
||||
Editors:
|
||||
@@ -1069,6 +1069,9 @@ After that, we can look at waste related to algorithms and requirements, but tha
|
||||
|
||||
Many more specific rules aim at the overall goals of simplicity and elimination of gratuitous waste.
|
||||
|
||||
* Flag an unused return value from a user-defined non-defaulted postfix `operator++` or `operator--` function. Prefer using the prefix form instead. (Note: "User-defined non-defaulted" is intended to reduce noise. Review this enforcement if it's still too noisy in practice.)
|
||||
|
||||
|
||||
### <a name="Rp-mutable"></a>P.10: Prefer immutable data to mutable data
|
||||
|
||||
##### Reason
|
||||
@@ -1439,7 +1442,7 @@ Consider:
|
||||
|
||||
set_settings(true, false, 42); // what do the numbers specify?
|
||||
|
||||
The parameter types and their values do not communicate what settings are being specified or what those values mean.
|
||||
The parameter types and their values do not communicate what settings are being specified or what those values mean.
|
||||
|
||||
This design is more explicit, safe and legible:
|
||||
|
||||
@@ -3324,7 +3327,7 @@ When I call `length(s)` should I check if `s` is `nullptr` first? Should the imp
|
||||
|
||||
##### Note
|
||||
|
||||
`zstring` do not represent ownership.
|
||||
`zstring` does not represent ownership.
|
||||
|
||||
**See also**: [Support library](#S-gsl)
|
||||
|
||||
@@ -3606,7 +3609,7 @@ Flag functions where no `return` expression could yield `nullptr`
|
||||
It's asking to return a reference to a destroyed temporary object.
|
||||
A `&&` is a magnet for temporary objects.
|
||||
|
||||
##### Example
|
||||
##### Example
|
||||
|
||||
A returned rvalue reference goes out of scope at the end of the full expression to which it is returned:
|
||||
|
||||
@@ -3806,7 +3809,7 @@ There is not a choice when a set of functions are used to do a semantically equi
|
||||
|
||||
##### Enforcement
|
||||
|
||||
???
|
||||
* Warn on an overload set where the overloads have a common prefix of parameters (e.g., `f(int)`, `f(int, const string&)`, `f(int, const string&, double)`). (Note: Review this enforcement if it's too noisy in practice.)
|
||||
|
||||
### <a name="Rf-reference-capture"></a>F.52: Prefer capturing by reference in lambdas that will be used locally, including passed to algorithms
|
||||
|
||||
@@ -5572,7 +5575,7 @@ An initialization explicitly states that initialization, rather than assignment,
|
||||
class A { // Good
|
||||
string s1;
|
||||
public:
|
||||
A(czstring p) : s1{p} { } // GOOD: directly construct (and the C-sting is explicitly named)
|
||||
A(czstring p) : s1{p} { } // GOOD: directly construct (and the C-string is explicitly named)
|
||||
// ...
|
||||
};
|
||||
|
||||
@@ -6596,8 +6599,8 @@ In particular, `std::vector` and `std::map` provide useful relatively simple mod
|
||||
std::vector<T> rep; // use a std::vector to hold elements
|
||||
};
|
||||
|
||||
template<typename T> bool operator==(const T&);
|
||||
template<typename T> bool operator!=(const T&);
|
||||
template<typename T> bool operator==(const Sorted_vector<T>&, const Sorted_vector<T>&);
|
||||
template<typename T> bool operator!=(const Sorted_vector<T>&, const Sorted_vector<T>&);
|
||||
// ...
|
||||
|
||||
Here, the STL style is followed, but incompletely.
|
||||
@@ -6816,7 +6819,7 @@ Do *not* represent non-hierarchical domain concepts as class hierarchies.
|
||||
|
||||
Here most overriding classes cannot implement most of the functions required in the interface well.
|
||||
Thus the base class becomes an implementation burden.
|
||||
Furthermore, the user of `Container` cannot rely on the member functions actually performing a meaningful operations reasonably efficiently;
|
||||
Furthermore, the user of `Container` cannot rely on the member functions actually performing meaningful operations reasonably efficiently;
|
||||
it may throw an exception instead.
|
||||
Thus users have to resort to run-time checking and/or
|
||||
not using this (over)general interface in favor of a particular interface found by a run-time type inquiry (e.g., a `dynamic_cast`).
|
||||
@@ -6898,7 +6901,7 @@ Such as on an ABI (link) boundary.
|
||||
};
|
||||
|
||||
A user can now use `D1`s and `D2`s interchangeably through the interface provided by `Device`.
|
||||
Furthermore, we can update `D1` and `D2` in a ways that are not binary compatible with older versions as long as all access goes through `Device`.
|
||||
Furthermore, we can update `D1` and `D2` in ways that are not binary compatible with older versions as long as all access goes through `Device`.
|
||||
|
||||
##### Enforcement
|
||||
|
||||
@@ -7017,8 +7020,8 @@ We want to eliminate two particular classes of errors:
|
||||
|
||||
##### Reason
|
||||
|
||||
Implementation details in an interface makes the interface brittle;
|
||||
that is, makes its users vulnerable to having to recompile after changes in the implementation.
|
||||
Implementation details in an interface make the interface brittle;
|
||||
that is, make its users vulnerable to having to recompile after changes in the implementation.
|
||||
Data in a base class increases the complexity of implementing the base and can lead to replication of code.
|
||||
|
||||
##### Note
|
||||
@@ -7201,7 +7204,7 @@ There are now two hierarchies:
|
||||
* interface: Smiley -> Circle -> Shape
|
||||
* implementation: Impl::Smiley -> Impl::Circle -> Impl::Shape
|
||||
|
||||
Since each implementation derived from its interface as well as its implementation base class we get a lattice (DAG):
|
||||
Since each implementation is derived from its interface as well as its implementation base class we get a lattice (DAG):
|
||||
|
||||
Smiley -> Circle -> Shape
|
||||
^ ^ ^
|
||||
@@ -8483,7 +8486,7 @@ And, talking about "invisible", this code produced no output:
|
||||
|
||||
Wrap a `union` in a class together with a type field.
|
||||
|
||||
The soon-to-be-standard `variant` type (to be found in `<variant>`) does that for you:
|
||||
The C++17 `variant` type (found in `<variant>`) does that for you:
|
||||
|
||||
variant<int, double> v;
|
||||
v = 123; // v holds an int
|
||||
@@ -8581,9 +8584,9 @@ Saving programmers from having to write such code is one reason for including `v
|
||||
break;
|
||||
case Tag::text:
|
||||
new(&s) string(e.s); // placement new: explicit construct
|
||||
type = e.type;
|
||||
}
|
||||
|
||||
type = e.type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -8935,7 +8938,7 @@ Here, we ignore such cases.
|
||||
* [R.11: Avoid calling `new` and `delete` explicitly](#Rr-newdelete)
|
||||
* [R.12: Immediately give the result of an explicit resource allocation to a manager object](#Rr-immediate-alloc)
|
||||
* [R.13: Perform at most one explicit resource allocation in a single expression statement](#Rr-single-alloc)
|
||||
* [R.14: ??? array vs. pointer parameter](#Rr-ap)
|
||||
* [R.14: Avoid `[]` parameters, prefer `span`](#Rr-ap)
|
||||
* [R.15: Always overload matched allocation/deallocation pairs](#Rr-pair)
|
||||
|
||||
* <a name="Rr-summary-smartptrs"></a>Smart pointer rule summary:
|
||||
@@ -9376,21 +9379,25 @@ Write your own factory wrapper if there is not one already.
|
||||
|
||||
* Flag expressions with multiple explicit resource allocations (problem: how many direct resource allocations can we recognize?)
|
||||
|
||||
### <a name="Rr-ap"></a>R.14: ??? array vs. pointer parameter
|
||||
### <a name="Rr-ap"></a>R.14: Avoid `[]` parameters, prefer `span`
|
||||
|
||||
##### Reason
|
||||
|
||||
An array decays to a pointer, thereby losing its size, opening the opportunity for range errors.
|
||||
Use `span` to preserve size information.
|
||||
|
||||
##### Example
|
||||
|
||||
??? what do we recommend: f(int*[]) or f(int**) ???
|
||||
void f(int[]); // not recommended
|
||||
|
||||
void f(int*); // not recommended for multiple objects
|
||||
// (a pointer should point to a single object, do not subscript)
|
||||
|
||||
**Alternative**: Use `span` to preserve size information.
|
||||
void f(gsl::span<int>); // good, recommended
|
||||
|
||||
##### Enforcement
|
||||
|
||||
Flag `[]` parameters.
|
||||
Flag `[]` parameters. Use `span` instead.
|
||||
|
||||
### <a name="Rr-pair"></a>R.15: Always overload matched allocation/deallocation pairs
|
||||
|
||||
@@ -9786,7 +9793,7 @@ The following should not pass code review:
|
||||
f(*g_p);
|
||||
|
||||
// BAD: same reason, just passing it as a "this" pointer
|
||||
g_p->func();
|
||||
g_p->func();
|
||||
}
|
||||
|
||||
The fix is simple -- take a local copy of the pointer to "keep a ref count" for your call tree:
|
||||
@@ -10206,7 +10213,7 @@ Flag all uses of ALL CAPS. For older code, accept ALL CAPS for macro names and f
|
||||
|
||||
##### Reason
|
||||
|
||||
One-declaration-per line increases readability and avoids mistakes related to
|
||||
One declaration per line increases readability and avoids mistakes related to
|
||||
the C/C++ grammar. It also leaves room for a more descriptive end-of-line
|
||||
comment.
|
||||
|
||||
@@ -10254,7 +10261,7 @@ or:
|
||||
|
||||
int a = 7, b = 9, c, d = 10, e = 3;
|
||||
|
||||
In a long list of declarators is is easy to overlook an uninitialized variable.
|
||||
In a long list of declarators it is easy to overlook an uninitialized variable.
|
||||
|
||||
##### Enforcement
|
||||
|
||||
@@ -10371,7 +10378,7 @@ Reuse of a member name as a local variable can also be a problem:
|
||||
if (x) {
|
||||
int m = 9;
|
||||
// ...
|
||||
m = 99; // assign to member
|
||||
m = 99; // assign to local variable
|
||||
// ...
|
||||
}
|
||||
}
|
||||
@@ -10710,7 +10717,7 @@ Like the distinction between copy-initialization and direct-initialization itsel
|
||||
Z z1{}; // OK: direct initialization, so we use explicit constructor
|
||||
Z z2 = {}; // error: copy initialization, so we cannot use the explicit constructor
|
||||
|
||||
Use plain `{}`-initialization unless you specifically wants to disable explicit constructors.
|
||||
Use plain `{}`-initialization unless you specifically want to disable explicit constructors.
|
||||
|
||||
##### Note
|
||||
|
||||
@@ -11134,7 +11141,7 @@ Complicated expressions are error-prone.
|
||||
// bad: assignment hidden in subexpression
|
||||
while ((c = getc()) != -1)
|
||||
|
||||
// bad: two non-local variables assigned in a sub-expressions
|
||||
// bad: two non-local variables assigned in sub-expressions
|
||||
while ((cin >> c1, cin >> c2), c1 == c2)
|
||||
|
||||
// better, but possibly still too complicated
|
||||
@@ -11314,7 +11321,7 @@ Use a `span`:
|
||||
a[pos - 1] = 2; // OK
|
||||
}
|
||||
|
||||
Use a `at()`:
|
||||
Use `at()`:
|
||||
|
||||
void f3(array<int, 10> a, int pos) // ALTERNATIVE B: Use at() for access
|
||||
{
|
||||
@@ -11415,7 +11422,7 @@ If you want to pass an array, say so:
|
||||
##### Enforcement
|
||||
|
||||
* Flag any arithmetic operation on an expression of pointer type that results in a value of pointer type.
|
||||
* Flag any indexing expression on an expression or variable of array type (either static array or `std::array`) where the indexer is not a compile-time constant expression with a value between `0` or and the upper bound of the array.
|
||||
* Flag any indexing expression on an expression or variable of array type (either static array or `std::array`) where the indexer is not a compile-time constant expression with a value between `0` and the upper bound of the array.
|
||||
* Flag any expression that would rely on implicit conversion of an array type to a pointer type.
|
||||
|
||||
This rule is part of the [bounds-safety profile](#SS-bounds).
|
||||
@@ -11712,9 +11719,9 @@ If the variable is actually declared `const`, the result of "casting away `const
|
||||
|
||||
##### Example, bad
|
||||
|
||||
void f(const int& i)
|
||||
void f(const int& x)
|
||||
{
|
||||
const_cast<int&>(i) = 42; // BAD
|
||||
const_cast<int&>(x) = 42; // BAD
|
||||
}
|
||||
|
||||
static int i = 0;
|
||||
@@ -11849,7 +11856,7 @@ State that `cache` is mutable even for a `const` object:
|
||||
mutable Cache cache;
|
||||
};
|
||||
|
||||
An alternative solution would to store a pointer to the `cache`:
|
||||
An alternative solution would be to store a pointer to the `cache`:
|
||||
|
||||
class X { // OK, but slightly messier solution
|
||||
public:
|
||||
@@ -12017,7 +12024,7 @@ Direct resource management in application code is error-prone and tedious.
|
||||
|
||||
##### Note
|
||||
|
||||
also known as "No naked `new`!"
|
||||
This is also known as the rule of "No naked `new`!"
|
||||
|
||||
##### Example, bad
|
||||
|
||||
@@ -12057,8 +12064,8 @@ This example not only violates the [no naked `new` rule](#Res-new) as in the pre
|
||||
|
||||
##### Enforcement
|
||||
|
||||
* if the `new` and the `delete` is in the same scope, mistakes can be flagged.
|
||||
* if the `new` and the `delete` are in a constructor/destructor pair, mistakes can be flagged.
|
||||
* If the `new` and the `delete` are in the same scope, mistakes can be flagged.
|
||||
* If the `new` and the `delete` are in a constructor/destructor pair, mistakes can be flagged.
|
||||
|
||||
### <a name="Res-arr2"></a>ES.62: Don't compare pointers into different arrays
|
||||
|
||||
@@ -12068,7 +12075,7 @@ The result of doing so is undefined.
|
||||
|
||||
##### Example, bad
|
||||
|
||||
void f(int n)
|
||||
void f()
|
||||
{
|
||||
int a1[7];
|
||||
int a2[9];
|
||||
@@ -12107,7 +12114,7 @@ In the rare cases where the slicing was deliberate the code can be surprising.
|
||||
Circle c2 {{ "{{" }}1, 1}, 43};
|
||||
assign(c, c2); // oops, not the whole state is transferred
|
||||
assert(c == c2); // if we supply copying, we should also provide comparison,
|
||||
// but this will likely return false
|
||||
// but this will likely return false
|
||||
|
||||
The result will be meaningless because the center and radius will not be copied from `c` into `s`.
|
||||
The first defense against this is to [define the base class `Shape` not to allow this](#Rc-copy-virtual).
|
||||
@@ -12631,7 +12638,7 @@ Often, a loop that requires a `break` is a good candidate for a function (algori
|
||||
|
||||
???
|
||||
|
||||
Often. a loop that uses `continue` can equivalently and as clearly be expressed by an `if`-statement.
|
||||
Often, a loop that uses `continue` can equivalently and as clearly be expressed by an `if`-statement.
|
||||
|
||||
???
|
||||
|
||||
@@ -12853,7 +12860,7 @@ The loop control up front should enable correct reasoning about what is happenin
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if (skip) { skip = false; continue; }
|
||||
//
|
||||
if (/* something */) skip = true; // Better: using two variable for two concepts.
|
||||
if (/* something */) skip = true; // Better: using two variables for two concepts.
|
||||
//
|
||||
}
|
||||
|
||||
@@ -12886,7 +12893,7 @@ whereas `if (p != nullptr)` would be a long-winded workaround.
|
||||
|
||||
This rule is especially useful when a declaration is used as a condition
|
||||
|
||||
if (auto pc = dynamic_cast<Circle>(ps)) { ... } // execute is ps points to a kind of Circle, good
|
||||
if (auto pc = dynamic_cast<Circle>(ps)) { ... } // execute if ps points to a kind of Circle, good
|
||||
|
||||
if (auto pc = dynamic_cast<Circle>(ps); pc != nullptr) { ... } // not recommended
|
||||
|
||||
@@ -13434,7 +13441,7 @@ Often, you will be surprised.
|
||||
##### Reason
|
||||
|
||||
Because we often need to optimize the initial design.
|
||||
Because a design that ignore the possibility of later improvement is hard to change.
|
||||
Because a design that ignores the possibility of later improvement is hard to change.
|
||||
|
||||
##### Example
|
||||
|
||||
@@ -13545,7 +13552,7 @@ However, `lower_bound` still doesn't return enough information for all uses, so
|
||||
`equal_range` returns a `pair` of iterators specifying the first and one beyond last match.
|
||||
|
||||
auto r = equal_range(begin(c), end(c), 7);
|
||||
for (auto p = r.first(); p != r.second(), ++p)
|
||||
for (auto p = r.first; p != r.second; ++p)
|
||||
cout << *p << '\n';
|
||||
|
||||
Obviously, these three interfaces are implemented by the same basic code.
|
||||
@@ -13840,7 +13847,7 @@ There are several ways that this example could be made safe for a multi-threaded
|
||||
Code that is never run in a multi-threaded environment.
|
||||
|
||||
Be careful: there are many examples where code that was "known" to never run in a multi-threaded program
|
||||
was run as part of a multi-threaded program. Often years later.
|
||||
was run as part of a multi-threaded program, often years later.
|
||||
Typically, such programs lead to a painful effort to remove data races.
|
||||
Therefore, code that is never intended to run in a multi-threaded environment should be clearly labeled as such and ideally come with compile or run-time enforcement mechanisms to catch those usage bugs early.
|
||||
|
||||
@@ -13987,7 +13994,7 @@ Application concepts are easier to reason about.
|
||||
##### Example
|
||||
|
||||
void some_fun() {
|
||||
std::string msg, msg2;
|
||||
std::string msg, msg2;
|
||||
std::thread publisher([&] { msg = "Hello"; }); // bad: less expressive
|
||||
// and more error-prone
|
||||
auto pubtask = std::async([&] { msg2 = "Hello"; }); // OK
|
||||
@@ -14825,7 +14832,7 @@ Message passing rules summary:
|
||||
##### Reason
|
||||
|
||||
A `future` preserves the usual function call return semantics for asynchronous tasks.
|
||||
The is no explicit locking and both correct (value) return and error (exception) return are handled simply.
|
||||
There is no explicit locking and both correct (value) return and error (exception) return are handled simply.
|
||||
|
||||
##### Example
|
||||
|
||||
@@ -14844,7 +14851,7 @@ The is no explicit locking and both correct (value) return and error (exception)
|
||||
##### Reason
|
||||
|
||||
A `future` preserves the usual function call return semantics for asynchronous tasks.
|
||||
The is no explicit locking and both correct (value) return and error (exception) return are handled simply.
|
||||
There is no explicit locking and both correct (value) return and error (exception) return are handled simply.
|
||||
|
||||
##### Example
|
||||
|
||||
@@ -17659,7 +17666,7 @@ Typically, the solution is to make what would have been a nested class non-local
|
||||
List<int, My_allocator> lst2;
|
||||
|
||||
Some people found the idea that the `Link` no longer was hidden inside the list scary, so we named the technique
|
||||
[SCARY](http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2009/n2911.pdf).From that academic paper:
|
||||
[SCARY](http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2009/n2911.pdf).From that academic paper:
|
||||
"The acronym SCARY describes assignments and initializations that are Seemingly erroneous (appearing Constrained by conflicting generic parameters), but Actually work with the Right implementation (unconstrained bY the conflict due to minimized dependencies."
|
||||
|
||||
##### Enforcement
|
||||
@@ -20704,7 +20711,7 @@ Names with types encoded are either verbose or cryptic.
|
||||
prints // print a C-style string
|
||||
printi // print an int
|
||||
|
||||
Requiring techniques like Hungarian notation to encode a type in a name is needed in C, but is generally unnecessary and actively harmful in a strongly statically-typed language like C++, because the annotations get out of date (the warts are just like comments and rot just like them) and they interfere with good use of the language (use the same name and overload resolution instead).
|
||||
Requiring techniques like Hungarian notation to encode a type has been used in untyped languages, but is generally unnecessary and actively harmful in a strongly statically-typed language like C++, because the annotations get out of date (the warts are just like comments and rot just like them) and they interfere with good use of the language (use the same name and overload resolution instead).
|
||||
|
||||
##### Note
|
||||
|
||||
@@ -20723,7 +20730,7 @@ This is not harmful and does not fall under this guideline because it does not e
|
||||
|
||||
##### Note
|
||||
|
||||
Some styles distinguishes members from local variable, and/or from global variable.
|
||||
Some styles distinguish members from local variable, and/or from global variable.
|
||||
|
||||
struct S {
|
||||
int m_;
|
||||
@@ -20734,7 +20741,7 @@ This is not harmful and does not fall under this guideline because it does not e
|
||||
|
||||
##### Note
|
||||
|
||||
Like C++, some styles distinguishes types from non-types.
|
||||
Like C++, some styles distinguish types from non-types.
|
||||
For example, by capitalizing type names, but not the names of functions and variables.
|
||||
|
||||
typename<typename T>
|
||||
@@ -21440,7 +21447,7 @@ In summary, no post-construction technique is perfect. The worst techniques dodg
|
||||
|
||||
### <a name="Sd-dtor"></a>Discussion: Make base class destructors public and virtual, or protected and nonvirtual
|
||||
|
||||
Should destruction behave virtually? That is, should destruction through a pointer to a `base` class be allowed? If yes, then `base`'s destructor must be public in order to be callable, and virtual otherwise calling it results in undefined behavior. Otherwise, it should be protected so that only derived classes can invoke it in their own destructors, and nonvirtual since it doesn't need to behave virtually virtual.
|
||||
Should destruction behave virtually? That is, should destruction through a pointer to a `base` class be allowed? If yes, then `base`'s destructor must be public in order to be callable, and virtual otherwise calling it results in undefined behavior. Otherwise, it should be protected so that only derived classes can invoke it in their own destructors, and nonvirtual since it doesn't need to behave virtually.
|
||||
|
||||
##### Example
|
||||
|
||||
@@ -21811,7 +21818,7 @@ The `string`s of `v` are destroyed upon exit from `bad()` and so is `v` itself.
|
||||
|
||||
##### Enforcement
|
||||
|
||||
Most compilers already warn about simple cases and has the information to do more. Consider any pointer returned from a function suspect. Use containers, resource handles, and views (e.g., `span` known not to be resource handles) to lower the number of cases to be examined. For starters, consider every class with a destructor as resource handle.
|
||||
Most compilers already warn about simple cases and have the information to do more. Consider any pointer returned from a function suspect. Use containers, resource handles, and views (e.g., `span` known not to be resource handles) to lower the number of cases to be examined. For starters, consider every class with a destructor as resource handle.
|
||||
|
||||
### <a name="Cr-templates"></a>Discussion: Use templates to express containers (and other resource handles)
|
||||
|
||||
@@ -21913,7 +21920,7 @@ The Microsoft compiler's C++ code analysis contains a set of rules specifically
|
||||
# <a name="S-glossary"></a>Glossary
|
||||
|
||||
A relatively informal definition of terms used in the guidelines
|
||||
(based of the glossary in [Programming: Principles and Practice using C++](http://www.stroustrup.com/programming.html))
|
||||
(based off the glossary in [Programming: Principles and Practice using C++](http://www.stroustrup.com/programming.html))
|
||||
|
||||
More information on many topics about C++ can be found on the [Standard C++ Foundation](https://isocpp.org)'s site.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user