Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 52 additions & 34 deletions CppCoreGuidelines.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# <a name="main"></a>C++ Core Guidelines

January 3, 2022
June 13, 2022


Editors:
Expand Down Expand Up @@ -2927,31 +2927,11 @@ For advanced uses (only), where you really need to optimize for rvalues passed t

void sink(unique_ptr<widget>); // input only, and moves ownership of the widget

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.18](#Rf-consume) and [F.19](#Rf-forward)).
* Returning `const T&` from assignments and similar operations (see [F.47](#Rf-assignment-op).)

##### Example

Assuming that `Matrix` has move operations (possibly by keeping its elements in a `std::vector`):

Matrix operator+(const Matrix& a, const Matrix& b)
{
Matrix res;
// ... fill res with the sum ...
return res;
}

Matrix x = m1 + m2; // move constructor

y = m3 + m3; // move assignment
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.18](#Rf-consume) and [F.19](#Rf-forward)).

##### Notes

The return value optimization doesn't handle the assignment case, but the move assignment does.

A reference can be assumed to refer to a valid object (language rule).
There is no (legitimate) "null reference."
If you need the notion of an optional value, use a pointer, `std::optional`, or a special value used to denote "no value."
Expand Down Expand Up @@ -3104,6 +3084,26 @@ The argument against is that it prevents (very frequent) use of move semantics.
* 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).
* 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).

##### Example

Assuming that `Matrix` has move operations (possibly by keeping its elements in a `std::vector`):

Matrix operator+(const Matrix& a, const Matrix& b)
{
Matrix res;
// ... fill res with the sum ...
return res;
}

Matrix x = m1 + m2; // move constructor

y = m3 + m3; // move assignment


##### Note

The return value optimization doesn't handle the assignment case, but the move assignment does.

##### Example

struct Package { // exceptional case: expensive-to-move object
Expand Down Expand Up @@ -4993,12 +4993,19 @@ There is a lot of code that is non-specific about ownership.

##### Example

???
class legacy_class
{
foo* m_owning;
bar* m_observer;
}

The only way to determine ownership may be to dig through the code to look for
allocations. If a pointer or reference is owning, document it as owning.

##### Note

If the `T*` or `T&` is owning, mark it `owning`. If the `T*` is not owning, consider marking it `ptr`.
This will aid documentation and analysis.
Ownership should be clear in new code (and refactored legacy code) according to [R.20](#Rr-owner) for owned
pointers and [R.3](#Rr-ptr) for non-owned pointers. References should never own [R.4](#Rr-ref).

##### Enforcement

Expand Down Expand Up @@ -15629,7 +15636,7 @@ Error-handling rule summary:
* [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.15: Throw by value, catch exceptions from a hierarchy by reference](#Re-exception-ref)
* [E.16: Destructors, deallocation, and `swap` must never fail](#Re-never-fail)
* [E.16: Destructors, deallocation, `swap`, and exception type copy/move construction must never fail](#Re-never-fail)
* [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.19: Use a `final_action` object to express cleanup if no suitable resource handle is available](#Re-finally)
Expand Down Expand Up @@ -16096,11 +16103,11 @@ To rethrow a caught exception use `throw;` not `throw e;`. Using `throw e;` woul
* 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, `swap`, and exception type copy/move construction must never fail

##### Reason

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.
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.

##### Example, don't

Expand Down Expand Up @@ -16129,14 +16136,17 @@ The standard library assumes that destructors, deallocation functions (e.g., `op

##### Note

Deallocation functions, including `operator delete`, must be `noexcept`. `swap` functions must be `noexcept`.
Most destructors are implicitly `noexcept` by default.
Also, [make move operations `noexcept`](#Rc-move-noexcept).
* Deallocation functions, including `operator delete`, must be `noexcept`.
* `swap` functions must be `noexcept`.
* Most destructors are implicitly `noexcept` by default.
* Also, [make move operations `noexcept`](#Rc-move-noexcept).
* 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.
* 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.

##### Enforcement

Catch destructors, deallocation operations, and `swap`s that `throw`.
Catch such operations that are not `noexcept`.
* Catch destructors, deallocation operations, and `swap`s that `throw`.
* Catch such operations that are not `noexcept`.

**See also**: [discussion](#Sd-never-fail)

Expand Down Expand Up @@ -20308,6 +20318,14 @@ For writing to a file, there is rarely a need to `flush`.

##### Note

For string streams (specifically `ostringstream`), the insertion of an `endl` is entirely equivalent
to the insertion of a `'\n'` character, but also in this case, `endl` might be significantly slower.

`endl` does *not* take care of producing a platform specific end-of-line sequence (like "\r\n" on
Windows). So for a string stream, `s << endl` just inserts a *single* character, `'\n'`.

##### Note

Apart from the (occasionally important) issue of performance,
the choice between `'\n'` and `endl` is almost completely aesthetic.

Expand Down
4 changes: 4 additions & 0 deletions scripts/hunspell/isocpp.dic
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ Lakos
Lakos96
Lavavej
LCSD05
legacy_class
lifecycle
*life-time
linearization
Expand All @@ -301,6 +302,8 @@ lvalues
m1
m2
m3
m_owning;
m_observer;
macros2
malloc
mallocfree
Expand Down Expand Up @@ -389,6 +392,7 @@ optimizable
O'Reilly
org
ostream
ostringstream
overabstract
overconstrain
overconstrained
Expand Down