update from master

This commit is contained in:
Sergey Zubkov
2022-01-03 17:07:19 -05:00
parent 202412e484
commit c34bd653b9

View File

@@ -1,6 +1,6 @@
# <a name="main"></a>C++ Core Guidelines # <a name="main"></a>C++ Core Guidelines
August 19, 2021 January 3, 2022
Editors: Editors:
@@ -3078,12 +3078,13 @@ Such older advice is now obsolete; it does not add value, and it interferes with
const vector<int> fct(); // bad: that "const" is more trouble than it is worth const vector<int> fct(); // bad: that "const" is more trouble than it is worth
vector<int> g(const vector<int>& vx) void g(vector<int>& vx)
{ {
// ... // ...
fct() = vx; // prevented by the "const" fct() = vx; // prevented by the "const"
// ... // ...
return fct(); // expensive copy: move semantics suppressed by the "const" vx = fct(); // expensive copy: move semantics suppressed by the "const"
// ...
} }
The argument for adding `const` to a return value is that it prevents (very rare) accidental access to a temporary. The argument for adding `const` to a return value is that it prevents (very rare) accidental access to a temporary.
@@ -5127,6 +5128,7 @@ We can imagine one case where you could want a protected virtual destructor: Whe
##### Enforcement ##### Enforcement
* A class with any virtual functions should have a destructor that is either public and virtual or else protected and non-virtual. * A class with any virtual functions should have a destructor that is either public and virtual or else protected and non-virtual.
* If a class inherits publicly from a base class, the base class should have a destructor that is either public and virtual or else protected and non-virtual.
### <a name="Rc-dtor-fail"></a>C.36: A destructor must not fail ### <a name="Rc-dtor-fail"></a>C.36: A destructor must not fail
@@ -7478,17 +7480,23 @@ Copying a polymorphic class is discouraged due to the slicing problem, see [C.67
class B { class B {
public: public:
virtual owner<B*> clone() = 0;
B() = default; B() = default;
virtual ~B() = default; virtual ~B() = default;
B(const B&) = delete; virtual gsl::owner<B*> clone() const = 0;
B& operator=(const B&) = delete; protected:
B(const B&) = default;
B& operator=(const B&) = default;
B(B&&) = default;
B& operator=(B&&) = default;
// ...
}; };
class D : public B { class D : public B {
public: public:
owner<D*> clone() override; gsl::owner<D*> clone() const override
~D() override; {
return new D{*this};
};
}; };
Generally, it is recommended to use smart pointers to represent ownership (see [R.20](#Rr-owner)). However, because of language rules, the covariant return type cannot be a smart pointer: `D::clone` can't return a `unique_ptr<D>` while `B::clone` returns `unique_ptr<B>`. Therefore, you either need to consistently return `unique_ptr<B>` in all overrides, or use `owner<>` utility from the [Guidelines Support Library](#SS-views). Generally, it is recommended to use smart pointers to represent ownership (see [R.20](#Rr-owner)). However, because of language rules, the covariant return type cannot be a smart pointer: `D::clone` can't return a `unique_ptr<D>` while `B::clone` returns `unique_ptr<B>`. Therefore, you either need to consistently return `unique_ptr<B>` in all overrides, or use `owner<>` utility from the [Guidelines Support Library](#SS-views).
@@ -8061,7 +8069,10 @@ Casting to a reference expresses that you intend to end up with a valid object,
##### Example ##### Example
??? std::string f(Base& b)
{
return dynamic_cast<Derived&>(b).to_string();
}
##### Enforcement ##### Enforcement
@@ -8809,12 +8820,12 @@ If you wanted to see the bytes of an `int`, use a (named) cast:
void if_you_must_pun(int& x) void if_you_must_pun(int& x)
{ {
auto p = reinterpret_cast<unsigned char*>(&x); auto p = reinterpret_cast<std::byte*>(&x);
cout << p[0] << '\n'; // OK; better cout << p[0] << '\n'; // OK; better
// ... // ...
} }
Accessing the result of a `reinterpret_cast` to a type different from the object's declared type is defined behavior. (Using `reinterpret_cast` is discouraged, Accessing the result of a `reinterpret_cast` from the object's declared type to `char*`, `unsigned char*`, or `std::byte*` is defined behavior. (Using `reinterpret_cast` is discouraged,
but at least we can see that something tricky is going on.) but at least we can see that something tricky is going on.)
##### Note ##### Note
@@ -9135,7 +9146,7 @@ Here, we ignore such cases.
* [R.31: If you have non-`std` smart pointers, follow the basic pattern from `std`](#Rr-smart) * [R.31: If you have non-`std` smart pointers, follow the basic pattern from `std`](#Rr-smart)
* [R.32: Take a `unique_ptr<widget>` parameter to express that a function assumes ownership of a `widget`](#Rr-uniqueptrparam) * [R.32: Take a `unique_ptr<widget>` parameter to express that a function assumes ownership of a `widget`](#Rr-uniqueptrparam)
* [R.33: Take a `unique_ptr<widget>&` parameter to express that a function reseats the `widget`](#Rr-reseat) * [R.33: Take a `unique_ptr<widget>&` parameter to express that a function reseats the `widget`](#Rr-reseat)
* [R.34: Take a `shared_ptr<widget>` parameter to express that a function is part owner](#Rr-sharedptrparam-owner) * [R.34: Take a `shared_ptr<widget>` parameter to express shared ownership](#Rr-sharedptrparam-owner)
* [R.35: Take a `shared_ptr<widget>&` parameter to express that a function might reseat the shared pointer](#Rr-sharedptrparam) * [R.35: Take a `shared_ptr<widget>&` parameter to express that a function might reseat the shared pointer](#Rr-sharedptrparam)
* [R.36: Take a `const shared_ptr<widget>&` parameter to express that it might retain a reference count to the object ???](#Rr-sharedptrparam-const) * [R.36: Take a `const shared_ptr<widget>&` parameter to express that it might retain a reference count to the object ???](#Rr-sharedptrparam-const)
* [R.37: Do not pass a pointer or reference obtained from an aliased smart pointer](#Rr-smartptrget) * [R.37: Do not pass a pointer or reference obtained from an aliased smart pointer](#Rr-smartptrget)
@@ -9810,7 +9821,7 @@ Using `unique_ptr` in this way both documents and enforces the function call's r
* (Simple) Warn if a function takes a `Unique_pointer<T>` parameter by lvalue reference and does not either assign to it or call `reset()` on it on at least one code path. Suggest taking a `T*` or `T&` instead. * (Simple) Warn if a function takes a `Unique_pointer<T>` parameter by lvalue reference and does not either assign to it or call `reset()` on it on at least one code path. Suggest taking a `T*` or `T&` instead.
* (Simple) ((Foundation)) Warn if a function takes a `Unique_pointer<T>` parameter by reference to `const`. Suggest taking a `const T*` or `const T&` instead. * (Simple) ((Foundation)) Warn if a function takes a `Unique_pointer<T>` parameter by reference to `const`. Suggest taking a `const T*` or `const T&` instead.
### <a name="Rr-sharedptrparam-owner"></a>R.34: Take a `shared_ptr<widget>` parameter to express that a function is part owner ### <a name="Rr-sharedptrparam-owner"></a>R.34: Take a `shared_ptr<widget>` parameter to express shared ownership
##### Reason ##### Reason
@@ -9818,11 +9829,16 @@ This makes the function's ownership sharing explicit.
##### Example, good ##### Example, good
void share(shared_ptr<widget>); // share -- "will" retain refcount class WidgetUser
{
void may_share(const shared_ptr<widget>&); // "might" retain refcount public:
// WidgetUser will share ownership of the widget
void reseat(shared_ptr<widget>&); // "might" reseat ptr explicit WidgetUser(std::shared_ptr<widget> w) noexcept:
m_widget{std::move(w)} {}
// ...
private:
std::shared_ptr<widget> m_widget;
};
##### Enforcement ##### Enforcement
@@ -9842,11 +9858,11 @@ This makes the function's reseating explicit.
##### Example, good ##### Example, good
void share(shared_ptr<widget>); // share -- "will" retain refcount void ChangeWidget(std::shared_ptr<widget>& w)
{
void reseat(shared_ptr<widget>&); // "might" reseat ptr // This will change the callers widget
w = std::make_shared<widget>(widget{});
void may_share(const shared_ptr<widget>&); // "might" retain refcount }
##### Enforcement ##### Enforcement
@@ -10196,7 +10212,10 @@ In this case, it might be a good idea to factor out the read:
##### Reason ##### Reason
Readability. Minimize resource retention. Readability.
Limit the loop variable visibility to the scope of the loop.
Avoid using the loop variable for other purposes after the loop.
Minimize resource retention.
##### Example ##### Example
@@ -10217,11 +10236,24 @@ Readability. Minimize resource retention.
} }
} }
##### Example, don't
int j; // BAD: j is visible outside the loop
for (j = 0; j < 100; ++j) {
// ...
}
// j is still visible here and isn't needed
**See also**: [Don't use a variable for two unrelated purposes](#Res-recycle)
##### Enforcement ##### Enforcement
* Flag loop variables declared before the loop and not used after the loop * Warn when a variable modified inside the `for`-statement is declared outside the loop and not being used outside the loop.
* (hard) Flag loop variables declared before the loop and used after the loop for an unrelated purpose. * (hard) Flag loop variables declared before the loop and used after the loop for an unrelated purpose.
**Discussion**: Scoping the loop variable to the loop body also helps code optimizers greatly. Recognizing that the induction variable
is only accessible in the loop body unblocks optimizations such as hoisting, strength reduction, loop-invariant code motion, etc.
##### C++17 and C++20 example ##### C++17 and C++20 example
Note: C++17 and C++20 also add `if`, `switch`, and range-`for` initializer statements. These require C++17 and C++20 support. Note: C++17 and C++20 also add `if`, `switch`, and range-`for` initializer statements. These require C++17 and C++20 support.
@@ -10239,8 +10271,6 @@ Note: C++17 and C++20 also add `if`, `switch`, and range-`for` initializer state
* Flag selection/loop variables declared before the body and not used after the body * Flag selection/loop variables declared before the body and not used after the body
* (hard) Flag selection/loop variables declared before the body and used after the body for an unrelated purpose. * (hard) Flag selection/loop variables declared before the body and used after the body for an unrelated purpose.
### <a name="Res-name-length"></a>ES.7: Keep common and local names short, and keep uncommon and non-local names longer ### <a name="Res-name-length"></a>ES.7: Keep common and local names short, and keep uncommon and non-local names longer
##### Reason ##### Reason
@@ -10439,7 +10469,10 @@ Flag variable and constant declarations with multiple declarators (e.g., `int* p
Consider: Consider:
auto p = v.begin(); // vector<int>::iterator auto p = v.begin(); // vector<DataRecord>::iterator
auto z1 = v[3]; // makes copy of DataRecord
auto& z2 = v[3]; // avoids copy
const auto& z3 = v[3]; // const and avoids copy
auto h = t.future(); auto h = t.future();
auto q = make_unique<int[]>(s); auto q = make_unique<int[]>(s);
auto f = [](int x) { return x + 10; }; auto f = [](int x) { return x + 10; };
@@ -10469,7 +10502,9 @@ When concepts become available, we can (and should) be more specific about the t
##### Example (C++17) ##### Example (C++17)
auto [ quotient, remainder ] = div(123456, 73); // break out the members of the div_t result std::set<int> values;
// ...
auto [ position, newly_inserted ] = values.insert(5); // break out the members of the std::pair
##### Enforcement ##### Enforcement
@@ -11699,17 +11734,24 @@ A key example is basic narrowing:
The guidelines support library offers a `narrow_cast` operation for specifying that narrowing is acceptable and a `narrow` ("narrow if") that throws an exception if a narrowing would throw away legal values: The guidelines support library offers a `narrow_cast` operation for specifying that narrowing is acceptable and a `narrow` ("narrow if") that throws an exception if a narrowing would throw away legal values:
i = narrow_cast<int>(d); // OK (you asked for it): narrowing: i becomes 7 i = gsl::narrow_cast<int>(d); // OK (you asked for it): narrowing: i becomes 7
i = narrow<int>(d); // OK: throws narrowing_error i = gsl::narrow<int>(d); // OK: throws narrowing_error
We also include lossy arithmetic casts, such as from a negative floating point type to an unsigned integral type: We also include lossy arithmetic casts, such as from a negative floating point type to an unsigned integral type:
double d = -7.9; double d = -7.9;
unsigned u = 0; unsigned u = 0;
u = d; // BAD u = d; // bad: narrowing
u = narrow_cast<unsigned>(d); // OK (you asked for it): u becomes 4294967289 u = gsl::narrow_cast<unsigned>(d); // OK (you asked for it): u becomes 4294967289
u = narrow<unsigned>(d); // OK: throws narrowing_error u = gsl::narrow<unsigned>(d); // OK: throws narrowing_error
##### Note
This rule does not apply to [contextual conversions to bool](https://en.cppreference.com/w/cpp/language/implicit_conversion#Contextual_conversions):
if (ptr) do_something(*ptr); // OK: ptr is used as a condition
bool b = ptr; // bad: narrowing
##### Enforcement ##### Enforcement
@@ -11745,7 +11787,7 @@ Flag uses of `0` and `NULL` for pointers. The transformation might be helped by
##### Reason ##### Reason
Casts are a well-known source of errors. Make some optimizations unreliable. Casts are a well-known source of errors and make some optimizations unreliable.
##### Example, bad ##### Example, bad
@@ -12690,39 +12732,7 @@ Flag actions in `for`-initializers and `for`-increments that do not relate to th
### <a name="Res-for-init"></a>ES.74: Prefer to declare a loop variable in the initializer part of a `for`-statement ### <a name="Res-for-init"></a>ES.74: Prefer to declare a loop variable in the initializer part of a `for`-statement
##### Reason See [ES.6](#Res-cond)
Limit the loop variable visibility to the scope of the loop.
Avoid using the loop variable for other purposes after the loop.
##### Example
for (int i = 0; i < 100; ++i) { // GOOD: i var is visible only inside the loop
// ...
}
##### Example, don't
int j; // BAD: j is visible outside the loop
for (j = 0; j < 100; ++j) {
// ...
}
// j is still visible here and isn't needed
**See also**: [Don't use a variable for two unrelated purposes](#Res-recycle)
##### Example
for (string s; cin >> s; ) {
cout << s << '\n';
}
##### Enforcement
Warn when a variable modified inside the `for`-statement is declared outside the loop and not being used outside the loop.
**Discussion**: Scoping the loop variable to the loop body also helps code optimizers greatly. Recognizing that the induction variable
is only accessible in the loop body unblocks optimizations such as hoisting, strength reduction, loop-invariant code motion, etc.
### <a name="Res-do"></a>ES.75: Avoid `do`-statements ### <a name="Res-do"></a>ES.75: Avoid `do`-statements
@@ -14101,7 +14111,7 @@ Unless you do, nothing is guaranteed to work and subtle errors will persist.
##### Note ##### Note
In a nutshell, if two threads can access the same object concurrently (without synchronization), and at least one is a writer (performing a non-`const` operation), you have a data race. In a nutshell, if two threads can access the same object concurrently (without synchronization), and at least one is a writer (performing a non-`const` operation), you have a data race.
For further information of how to use synchronization well to eliminate data races, please consult a good book about concurrency. For further information of how to use synchronization well to eliminate data races, please consult a good book about concurrency (See [Carefully study the literature](#Rconc-literature)).
##### Example, bad ##### Example, bad
@@ -15401,7 +15411,7 @@ Become an expert before shipping lock-free code for others to use.
* Damian Dechev, Peter Pirkelbauer, and Bjarne Stroustrup: Understanding and Effectively Preventing the ABA Problem in Descriptor-based Lock-free Designs. 13th IEEE Computer Society ISORC 2010 Symposium. May 2010. * Damian Dechev, Peter Pirkelbauer, and Bjarne Stroustrup: Understanding and Effectively Preventing the ABA Problem in Descriptor-based Lock-free Designs. 13th IEEE Computer Society ISORC 2010 Symposium. May 2010.
* Damian Dechev and Bjarne Stroustrup: Scalable Non-blocking Concurrent Objects for Mission Critical Code. ACM OOPSLA'09. October 2009 * Damian Dechev and Bjarne Stroustrup: Scalable Non-blocking Concurrent Objects for Mission Critical Code. ACM OOPSLA'09. October 2009
* Damian Dechev, Peter Pirkelbauer, Nicolas Rouquette, and Bjarne Stroustrup: Semantically Enhanced Containers for Concurrent Real-Time Systems. Proc. 16th Annual IEEE International Conference and Workshop on the Engineering of Computer Based Systems (IEEE ECBS). April 2009. * Damian Dechev, Peter Pirkelbauer, Nicolas Rouquette, and Bjarne Stroustrup: Semantically Enhanced Containers for Concurrent Real-Time Systems. Proc. 16th Annual IEEE International Conference and Workshop on the Engineering of Computer Based Systems (IEEE ECBS). April 2009.
* Maurice Herlihy, Nir Shavit, Victor Luchangco, Michael Spear, "The Art of Multiprocessor Programming", 2nd ed. September 2020
### <a name="Rconc-double"></a>CP.110: Do not write your own double-checked locking for initialization ### <a name="Rconc-double"></a>CP.110: Do not write your own double-checked locking for initialization
@@ -15608,7 +15618,7 @@ Error-handling rule summary:
* [E.12: Use `noexcept` when exiting a function because of a `throw` is impossible or unacceptable](#Re-noexcept) * [E.12: Use `noexcept` when exiting a function because of a `throw` is impossible or unacceptable](#Re-noexcept)
* [E.13: Never throw while being the direct owner of an object](#Re-never-throw) * [E.13: Never throw while being the direct owner of an object](#Re-never-throw)
* [E.14: Use purpose-designed user-defined types as exceptions (not built-in types)](#Re-exception-types) * [E.14: Use purpose-designed user-defined types as exceptions (not built-in types)](#Re-exception-types)
* [E.15: Catch exceptions from a hierarchy by reference](#Re-exception-ref) * [E.15: Throw by value, catch exceptions from a hierarchy reference](#Re-exception-ref)
* [E.16: Destructors, deallocation, and `swap` must never fail](#Re-never-fail) * [E.16: Destructors, deallocation, and `swap` must never fail](#Re-never-fail)
* [E.17: Don't try to catch every exception in every function](#Re-not-always) * [E.17: Don't try to catch every exception in every function](#Re-not-always)
* [E.18: Minimize the use of explicit `try`/`catch`](#Re-catch) * [E.18: Minimize the use of explicit `try`/`catch`](#Re-catch)
@@ -16057,41 +16067,48 @@ The standard-library classes derived from `exception` should be used only as bas
Catch `throw` and `catch` of a built-in type. Maybe warn about `throw` and `catch` using a standard-library `exception` type. Obviously, exceptions derived from the `std::exception` hierarchy are fine. Catch `throw` and `catch` of a built-in type. Maybe warn about `throw` and `catch` using a standard-library `exception` type. Obviously, exceptions derived from the `std::exception` hierarchy are fine.
### <a name="Re-exception-ref"></a>E.15: Catch exceptions from a hierarchy by reference ### <a name="Re-exception-ref"></a>E.15: Throw by value, catch exceptions from a hierarchy reference
##### Reason ##### Reason
To prevent slicing. Throwing by value (not by pointer) and catching by reference prevents copying, especially slicing base subobjects.
##### Example ##### Example; bad
void f() void f()
{ {
try { try {
// ... // ...
throw new widget{}; // don't: throw by value not by raw pointer
// ...
} }
catch (exception e) { // don't: might slice catch (base_class e) { // don't: might slice
// ... // ...
} }
} }
Instead, use a reference: Instead, use a reference:
catch (exception& e) { /* ... */ } catch (base_class& e) { /* ... */ }
or - typically better still - a `const` reference: or - typically better still - a `const` reference:
catch (const exception& e) { /* ... */ } catch (const base_class& e) { /* ... */ }
Most handlers do not modify their exception and in general we [recommend use of `const`](#Res-const). Most handlers do not modify their exception and in general we [recommend use of `const`](#Res-const).
##### Note ##### Note
Catch by value can be appropriate for a small value type such as an `enum` value.
##### Note
To rethrow a caught exception use `throw;` not `throw e;`. Using `throw e;` would throw a new copy of `e` (sliced to the static type `std::exception`) instead of rethrowing the original exception of type `std::runtime_error`. (But keep [Don't try to catch every exception in every function](#Re-not-always) and [Minimize the use of explicit `try`/`catch`](#Re-catch) in mind.) To rethrow a caught exception use `throw;` not `throw e;`. Using `throw e;` would throw a new copy of `e` (sliced to the static type `std::exception`) instead of rethrowing the original exception of type `std::runtime_error`. (But keep [Don't try to catch every exception in every function](#Re-not-always) and [Minimize the use of explicit `try`/`catch`](#Re-catch) in mind.)
##### Enforcement ##### Enforcement
Flag by-value exceptions if their types are part of a hierarchy (could require whole-program analysis to be perfect). * Flag catching by value of a type that has a virtual function.
* Flag throwing raw pointers.
### <a name="Re-never-fail"></a>E.16: Destructors, deallocation, and `swap` must never fail ### <a name="Re-never-fail"></a>E.16: Destructors, deallocation, and `swap` must never fail
@@ -21181,8 +21198,8 @@ A `char*` that points to more than one `char` but is not a C-style string (e.g.,
* `czstring` // a `const char*` supposed to be a C-style string; that is, a zero-terminated sequence of `const` `char` or `nullptr` * `czstring` // a `const char*` supposed to be a C-style string; that is, a zero-terminated sequence of `const` `char` or `nullptr`
Logically, those last two aliases are not needed, but we are not always logical, and they make the distinction between a pointer to one `char` and a pointer to a C-style string explicit. Logically, those last two aliases are not needed, but we are not always logical, and they make the distinction between a pointer to one `char` and a pointer to a C-style string explicit.
A sequence of characters that is not assumed to be zero-terminated should be a `char*`, rather than a `zstring`. A sequence of characters that is not assumed to be zero-terminated should be a `span<char>`, or if that is impossible because of ABI issues a `char*`, rather than a `zstring`.
French accent optional.
Use `not_null<zstring>` for C-style strings that cannot be `nullptr`. ??? Do we need a name for `not_null<zstring>`? or is its ugliness a feature? Use `not_null<zstring>` for C-style strings that cannot be `nullptr`. ??? Do we need a name for `not_null<zstring>`? or is its ugliness a feature?