This commit is contained in:
Sergey Zubkov
2020-04-02 11:32:29 -04:00
parent ae2b5db57a
commit c0e536c8f3

View File

@@ -1308,6 +1308,14 @@ Non-`const` global variables hide dependencies and make the dependencies subject
Who else might modify `data`? Who else might modify `data`?
**Warning**: The initialization of global objects is not totally ordered.
If you use a global object initialize it with a constant.
Note that it is possible to get undefined initialization order even for `const` objects.
##### Exception
A global object is often better than a singleton.
##### Note ##### Note
Global constants are useful. Global constants are useful.
@@ -1319,9 +1327,12 @@ The rule against global variables applies to namespace scope variables as well.
**Alternative**: If you use global (more generally namespace scope) data to avoid copying, consider passing the data as an object by reference to `const`. **Alternative**: If you use global (more generally namespace scope) data to avoid copying, consider passing the data as an object by reference to `const`.
Another solution is to define the data as the state of some object and the operations as member functions. Another solution is to define the data as the state of some object and the operations as member functions.
**Warning**: Beware of data races: If one thread can access nonlocal data (or data passed by reference) while another thread executes the callee, we can have a data race. **Warning**: Beware of data races: If one thread can access non-local data (or data passed by reference) while another thread executes the callee, we can have a data race.
Every pointer or reference to mutable data is a potential data race. Every pointer or reference to mutable data is a potential data race.
Using global pointers or references to access and change non-const, and otherwise non-global,
data isn't a better alternative to non-const global variables since that doesn't solve the issues of hidden dependencies or potential race conditions.
##### Note ##### Note
You cannot have a race condition on immutable data. You cannot have a race condition on immutable data.
@@ -1334,7 +1345,8 @@ The rule is "avoid", not "don't use." Of course there will be (rare) exceptions,
##### Enforcement ##### Enforcement
(Simple) Report all non-`const` variables declared at namespace scope. (Simple) Report all non-`const` variables declared at namespace scope and global pointers/references to non-const data.
### <a name="Ri-singleton"></a>I.3: Avoid singletons ### <a name="Ri-singleton"></a>I.3: Avoid singletons
@@ -1531,9 +1543,9 @@ Consider:
double sqrt(double x); double sqrt(double x);
Here `x` must be nonnegative. The type system cannot (easily and naturally) express that, so we must use other means. For example: Here `x` must be non-negative. The type system cannot (easily and naturally) express that, so we must use other means. For example:
double sqrt(double x); // x must be nonnegative double sqrt(double x); // x must be non-negative
Some preconditions can be expressed as assertions. For example: Some preconditions can be expressed as assertions. For example:
@@ -1592,7 +1604,7 @@ Once language support becomes available (e.g., see the [contract proposal](http:
##### Note ##### Note
No, using `unsigned` is not a good way to sidestep the problem of [ensuring that a value is nonnegative](#Res-nonnegative). No, using `unsigned` is not a good way to sidestep the problem of [ensuring that a value is non-negative](#Res-non-negative).
##### Enforcement ##### Enforcement
@@ -1811,7 +1823,7 @@ We don't consider "performance" a valid reason not to use exceptions.
* Often, explicit error checking and handling consume as much time and space as exception handling. * Often, explicit error checking and handling consume as much time and space as exception handling.
* Often, cleaner code yields better performance with exceptions (simplifying the tracing of paths through the program and their optimization). * Often, cleaner code yields better performance with exceptions (simplifying the tracing of paths through the program and their optimization).
* A good rule for performance critical code is to move checking outside the critical part of the code ([checking](#Rper-checking)). * A good rule for performance critical code is to move checking outside the [critical](#Rper-critical) part of the code.
* In the longer term, more regular code gets better optimized. * In the longer term, more regular code gets better optimized.
* Always carefully [measure](#Rper-measure) before making performance claims. * Always carefully [measure](#Rper-measure) before making performance claims.
@@ -1970,7 +1982,7 @@ This `draw2()` passes the same amount of information to `draw()`, but makes the
##### Exception ##### Exception
Use `zstring` and `czstring` to represent C-style, zero-terminated strings. Use `zstring` and `czstring` to represent C-style, zero-terminated strings.
But when doing so, use `std::string_view` or `string_span` from the [GSL](#GSL) to prevent range errors. But when doing so, use `std::string_view` or `string_span` from the [GSL](#S-gsl) to prevent range errors.
##### Enforcement ##### Enforcement
@@ -2212,7 +2224,7 @@ interface (widget.h)
void draw(); // public API that will be forwarded to the implementation void draw(); // public API that will be forwarded to the implementation
widget(int); // defined in the implementation file widget(int); // defined in the implementation file
~widget(); // defined in the implementation file, where impl is a complete type ~widget(); // defined in the implementation file, where impl is a complete type
widget(widget&&) = default; widget(widget&&); // defined in the implementation file
widget(const widget&) = delete; widget(const widget&) = delete;
widget& operator=(widget&&); // defined in the implementation file widget& operator=(widget&&); // defined in the implementation file
widget& operator=(const widget&) = delete; widget& operator=(const widget&) = delete;
@@ -2229,6 +2241,7 @@ implementation (widget.cpp)
}; };
void widget::draw() { pimpl->draw(*this); } void widget::draw() { pimpl->draw(*this); }
widget::widget(int n) : pimpl{std::make_unique<impl>(n)} {} widget::widget(int n) : pimpl{std::make_unique<impl>(n)} {}
widget::widget(widget&&) = default;
widget::~widget() = default; widget::~widget() = default;
widget& widget::operator=(widget&&) = default; widget& widget::operator=(widget&&) = default;
@@ -2359,7 +2372,7 @@ Other function rules:
* [F.50: Use a lambda when a function won't do (to capture local variables, or to write a local function)](#Rf-capture-vs-overload) * [F.50: Use a lambda when a function won't do (to capture local variables, or to write a local function)](#Rf-capture-vs-overload)
* [F.51: Where there is a choice, prefer default arguments over overloading](#Rf-default-args) * [F.51: Where there is a choice, prefer default arguments over overloading](#Rf-default-args)
* [F.52: Prefer capturing by reference in lambdas that will be used locally, including passed to algorithms](#Rf-reference-capture) * [F.52: Prefer capturing by reference in lambdas that will be used locally, including passed to algorithms](#Rf-reference-capture)
* [F.53: Avoid capturing by reference in lambdas that will be used nonlocally, including returned, stored on the heap, or passed to another thread](#Rf-value-capture) * [F.53: Avoid capturing by reference in lambdas that will be used non-locally, including returned, stored on the heap, or passed to another thread](#Rf-value-capture)
* [F.54: If you capture `this`, capture all variables explicitly (no default capture)](#Rf-this-capture) * [F.54: If you capture `this`, capture all variables explicitly (no default capture)](#Rf-this-capture)
* [F.55: Don't use `va_arg` arguments](#F-varargs) * [F.55: Don't use `va_arg` arguments](#F-varargs)
@@ -3122,7 +3135,7 @@ such as `string` and `vector`, that needs to do free store allocations.
To compare, if we passed out all values as return values, we would something like this: To compare, if we passed out all values as return values, we would something like this:
pair<istream&, string> get_string(istream& is); // not recommended pair<istream&, string> get_string(istream& is) // not recommended
{ {
string s; string s;
is >> s; is >> s;
@@ -3860,7 +3873,7 @@ This is a simple three-stage parallel pipeline. Each `stage` object encapsulates
Flag a lambda that captures by reference, but is used other than locally within the function scope or passed to a function by reference. (Note: This rule is an approximation, but does flag passing by pointer as those are more likely to be stored by the callee, writing to a heap location accessed via a parameter, returning the lambda, etc. The Lifetime rules will also provide general rules that flag escaping pointers and references including via lambdas.) Flag a lambda that captures by reference, but is used other than locally within the function scope or passed to a function by reference. (Note: This rule is an approximation, but does flag passing by pointer as those are more likely to be stored by the callee, writing to a heap location accessed via a parameter, returning the lambda, etc. The Lifetime rules will also provide general rules that flag escaping pointers and references including via lambdas.)
### <a name="Rf-value-capture"></a>F.53: Avoid capturing by reference in lambdas that will be used nonlocally, including returned, stored on the heap, or passed to another thread ### <a name="Rf-value-capture"></a>F.53: Avoid capturing by reference in lambdas that will be used non-locally, including returned, stored on the heap, or passed to another thread
##### Reason ##### Reason
@@ -4480,7 +4493,7 @@ Destructor rules:
* [C.31: All resources acquired by a class must be released by the class's destructor](#Rc-dtor-release) * [C.31: All resources acquired by a class must be released by the class's destructor](#Rc-dtor-release)
* [C.32: If a class has a raw pointer (`T*`) or reference (`T&`), consider whether it might be owning](#Rc-dtor-ptr) * [C.32: If a class has a raw pointer (`T*`) or reference (`T&`), consider whether it might be owning](#Rc-dtor-ptr)
* [C.33: If a class has an owning pointer member, define or `=delete` a destructor](#Rc-dtor-ptr2) * [C.33: If a class has an owning pointer member, define or `=delete` a destructor](#Rc-dtor-ptr2)
* [C.35: A base class destructor should be either public and virtual, or protected and nonvirtual](#Rc-dtor-virtual) * [C.35: A base class destructor should be either public and virtual, or protected and non-virtual](#Rc-dtor-virtual)
* [C.36: A destructor may not fail](#Rc-dtor-fail) * [C.36: A destructor may not fail](#Rc-dtor-fail)
* [C.37: Make destructors `noexcept`](#Rc-dtor-noexcept) * [C.37: Make destructors `noexcept`](#Rc-dtor-noexcept)
@@ -4688,7 +4701,7 @@ Users will be surprised if copy/move construction and copy/move assignment do lo
}; };
shared_ptr<Impl> p; shared_ptr<Impl> p;
public: public:
Silly(const Silly& a) : p{a.p} { *p = *a.p; } // deep copy Silly(const Silly& a) : p(make_shared<Impl>()) { *p = *a.p; } // deep copy
Silly& operator=(const Silly& a) { p = a.p; } // shallow copy Silly& operator=(const Silly& a) { p = a.p; } // shallow copy
// ... // ...
}; };
@@ -4921,7 +4934,7 @@ That would sometimes require non-trivial code changes and may affect ABIs.
* A class with an `owner<T>` should define its default operations. * A class with an `owner<T>` should define its default operations.
### <a name="Rc-dtor-virtual"></a>C.35: A base class destructor should be either public and virtual, or protected and nonvirtual ### <a name="Rc-dtor-virtual"></a>C.35: A base class destructor should be either public and virtual, or protected and non-virtual
##### Reason ##### Reason
@@ -4936,7 +4949,7 @@ See [this in the Discussion section](#Sd-dtor).
##### Example, bad ##### Example, bad
struct Base { // BAD: implicitly has a public nonvirtual destructor struct Base { // BAD: implicitly has a public non-virtual destructor
virtual void f(); virtual void f();
}; };
@@ -4959,7 +4972,7 @@ If the interface allows destroying, it should be safe to do so.
##### Note ##### Note
A destructor must be nonprivate or it will prevent using the type: A destructor must be non-private or it will prevent using the type:
class X { class X {
~X(); // private destructor ~X(); // private destructor
@@ -4979,7 +4992,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 nonvirtual. * A class with any virtual functions 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 may not fail ### <a name="Rc-dtor-fail"></a>C.36: A destructor may not fail
@@ -6376,7 +6389,7 @@ A `swap` can be handy for implementing a number of idioms, from smoothly moving
int m2; int m2;
}; };
Providing a nonmember `swap` function in the same namespace as your type for callers' convenience. Providing a non-member `swap` function in the same namespace as your type for callers' convenience.
void swap(Foo& a, Foo& b) void swap(Foo& a, Foo& b)
{ {
@@ -6775,7 +6788,7 @@ Summary:
* [F.50: Use a lambda when a function won't do (to capture local variables, or to write a local function)](#Rf-capture-vs-overload) * [F.50: Use a lambda when a function won't do (to capture local variables, or to write a local function)](#Rf-capture-vs-overload)
* [F.52: Prefer capturing by reference in lambdas that will be used locally, including passed to algorithms](#Rf-reference-capture) * [F.52: Prefer capturing by reference in lambdas that will be used locally, including passed to algorithms](#Rf-reference-capture)
* [F.53: Avoid capturing by reference in lambdas that will be used nonlocally, including returned, stored on the heap, or passed to another thread](#Rf-value-capture) * [F.53: Avoid capturing by reference in lambdas that will be used non-locally, including returned, stored on the heap, or passed to another thread](#Rf-value-capture)
* [ES.28: Use lambdas for complex initialization, especially of `const` variables](#Res-lambda-init) * [ES.28: Use lambdas for complex initialization, especially of `const` variables](#Res-lambda-init)
## <a name="SS-hier"></a>C.hier: Class hierarchies (OOP) ## <a name="SS-hier"></a>C.hier: Class hierarchies (OOP)
@@ -6883,7 +6896,7 @@ not using this (over)general interface in favor of a particular interface found
##### Enforcement ##### Enforcement
* Look for classes with lots of members that do nothing but throw. * Look for classes with lots of members that do nothing but throw.
* Flag every use of a nonpublic base class `B` where the derived class `D` does not override a virtual function or access a protected member in `B`, and `B` is not one of the following: empty, a template parameter or parameter pack of `D`, a class template specialized with `D`. * Flag every use of a non-public base class `B` where the derived class `D` does not override a virtual function or access a protected member in `B`, and `B` is not one of the following: empty, a template parameter or parameter pack of `D`, a class template specialized with `D`.
### <a name="Rh-abstract"></a>C.121: If a base class is used as an interface, make it a pure abstract class ### <a name="Rh-abstract"></a>C.121: If a base class is used as an interface, make it a pure abstract class
@@ -6989,13 +7002,13 @@ Flag abstract classes with constructors.
##### Reason ##### Reason
A class with a virtual function is usually (and in general) used via a pointer to base. Usually, the last user has to call delete on a pointer to base, often via a smart pointer to base, so the destructor should be public and virtual. Less commonly, if deletion through a pointer to base is not intended to be supported, the destructor should be protected and nonvirtual; see [C.35](#Rc-dtor-virtual). A class with a virtual function is usually (and in general) used via a pointer to base. Usually, the last user has to call delete on a pointer to base, often via a smart pointer to base, so the destructor should be public and virtual. Less commonly, if deletion through a pointer to base is not intended to be supported, the destructor should be protected and non-virtual; see [C.35](#Rc-dtor-virtual).
##### Example, bad ##### Example, bad
struct B { struct B {
virtual int f() = 0; virtual int f() = 0;
// ... no user-written destructor, defaults to public nonvirtual ... // ... no user-written destructor, defaults to public non-virtual ...
}; };
// bad: derived from a class without a virtual destructor // bad: derived from a class without a virtual destructor
@@ -7015,7 +7028,7 @@ There are people who don't follow this rule because they plan to use a class onl
##### Enforcement ##### Enforcement
* A class with any virtual functions should have a destructor that is either public and virtual or else protected and nonvirtual. * A class with any virtual functions should have a destructor that is either public and virtual or else protected and non-virtual.
* Flag `delete` of a class with a virtual function but no virtual destructor. * Flag `delete` of a class with a virtual function but no virtual destructor.
### <a name="Rh-override"></a>C.128: Virtual functions should specify exactly one of `virtual`, `override`, or `final` ### <a name="Rh-override"></a>C.128: Virtual functions should specify exactly one of `virtual`, `override`, or `final`
@@ -7062,7 +7075,7 @@ It's simple and clear:
We want to eliminate two particular classes of errors: We want to eliminate two particular classes of errors:
* **implicit virtual**: the programmer intended the function to be implicitly virtual and it is (but readers of the code can't tell); or the programmer intended the function to be implicitly virtual but it isn't (e.g., because of a subtle parameter list mismatch); or the programmer did not intend the function to be virtual but it is (because it happens to have the same signature as a virtual in the base class) * **implicit virtual**: the programmer intended the function to be implicitly virtual and it is (but readers of the code can't tell); or the programmer intended the function to be implicitly virtual but it isn't (e.g., because of a subtle parameter list mismatch); or the programmer did not intend the function to be virtual but it is (because it happens to have the same signature as a virtual in the base class)
* **implicit override**: the programmer intended the function to be implicitly an overrider and it is (but readers of the code can't tell); or the programmer intended the function to be implicitly an overrider but it isn't (e.g., because of a subtle parameter list mismatch); or the programmer did not intend the function to be an overrider but it is (because it happens to have the same signature as a virtual in the base class -- note this problem arises whether or not the function is explicitly declared virtual, because the programmer may have intended to create either a new virtual function or a new nonvirtual function) * **implicit override**: the programmer intended the function to be implicitly an overrider and it is (but readers of the code can't tell); or the programmer intended the function to be implicitly an overrider but it isn't (e.g., because of a subtle parameter list mismatch); or the programmer did not intend the function to be an overrider but it is (because it happens to have the same signature as a virtual in the base class -- note this problem arises whether or not the function is explicitly declared virtual, because the programmer may have intended to create either a new virtual function or a new non-virtual function)
##### Enforcement ##### Enforcement
@@ -7621,7 +7634,7 @@ Without a using declaration, member functions in the derived class hide the enti
##### Note ##### Note
This issue affects both virtual and nonvirtual member functions This issue affects both virtual and non-virtual member functions
For variadic bases, C++17 introduced a variadic form of the using-declaration, For variadic bases, C++17 introduced a variadic form of the using-declaration,
@@ -8063,7 +8076,7 @@ You cannot overload function objects.
Overload rule summary: Overload rule summary:
* [C.160: Define operators primarily to mimic conventional usage](#Ro-conventional) * [C.160: Define operators primarily to mimic conventional usage](#Ro-conventional)
* [C.161: Use nonmember functions for symmetric operators](#Ro-symmetric) * [C.161: Use non-member functions for symmetric operators](#Ro-symmetric)
* [C.162: Overload operations that are roughly equivalent](#Ro-equivalent) * [C.162: Overload operations that are roughly equivalent](#Ro-equivalent)
* [C.163: Overload only for operations that are roughly equivalent](#Ro-equivalent-2) * [C.163: Overload only for operations that are roughly equivalent](#Ro-equivalent-2)
* [C.164: Avoid implicit conversion operators](#Ro-conversion) * [C.164: Avoid implicit conversion operators](#Ro-conversion)
@@ -8098,19 +8111,19 @@ Here, the conventional semantics is maintained: [Copies compare equal](#SS-copy)
##### Note ##### Note
Nonmember operators should be either friends or defined in [the same namespace as their operands](#Ro-namespace). Non-member operators should be either friends or defined in [the same namespace as their operands](#Ro-namespace).
[Binary operators should treat their operands equivalently](#Ro-symmetric). [Binary operators should treat their operands equivalently](#Ro-symmetric).
##### Enforcement ##### Enforcement
Possibly impossible. Possibly impossible.
### <a name="Ro-symmetric"></a>C.161: Use nonmember functions for symmetric operators ### <a name="Ro-symmetric"></a>C.161: Use non-member functions for symmetric operators
##### Reason ##### Reason
If you use member functions, you need two. If you use member functions, you need two.
Unless you use a nonmember function for (say) `==`, `a == b` and `b == a` will be subtly different. Unless you use a non-member function for (say) `==`, `a == b` and `b == a` will be subtly different.
##### Example ##### Example
@@ -8407,7 +8420,7 @@ This is a special case of the rule that [helper functions should be defined in t
##### Enforcement ##### Enforcement
* Flag operator definitions that are not it the namespace of their operands * Flag operator definitions that are not in the namespace of their operands
### <a name="Ro-lambda"></a>C.170: If you feel like overloading a lambda, use a generic lambda ### <a name="Ro-lambda"></a>C.170: If you feel like overloading a lambda, use a generic lambda
@@ -8489,7 +8502,7 @@ But heed the warning: [Avoid "naked" `union`s](#Ru-naked)
~Immutable_string() ~Immutable_string()
{ {
if (size >= buffer_size) if (size >= buffer_size)
delete string_ptr; delete[] string_ptr;
} }
const char* get_str() const const char* get_str() const
@@ -8808,13 +8821,13 @@ To minimize surprises: traditional enums convert to int too readily.
void Print_color(int color); void Print_color(int color);
enum Web_color { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF }; enum Web_color { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF };
enum Product_info { Red = 0, Purple = 1, Blue = 2 }; enum Product_info { red = 0, purple = 1, blue = 2 };
Web_color webby = Web_color::blue; Web_color webby = Web_color::blue;
// Clearly at least one of these calls is buggy. // Clearly at least one of these calls is buggy.
Print_color(webby); Print_color(webby);
Print_color(Product_info::Blue); Print_color(Product_info::blue);
Instead use an `enum class`: Instead use an `enum class`:
@@ -8825,7 +8838,7 @@ Instead use an `enum class`:
Web_color webby = Web_color::blue; Web_color webby = Web_color::blue;
Print_color(webby); // Error: cannot convert Web_color to int. Print_color(webby); // Error: cannot convert Web_color to int.
Print_color(Product_info::Red); // Error: cannot convert Product_info to int. Print_color(Product_info::red); // Error: cannot convert Product_info to int.
##### Enforcement ##### Enforcement
@@ -9078,7 +9091,7 @@ What is `Port`? A handy wrapper that encapsulates the resource:
Where a resource is "ill-behaved" in that it isn't represented as a class with a destructor, wrap it in a class or use [`finally`](#Re-finally) Where a resource is "ill-behaved" in that it isn't represented as a class with a destructor, wrap it in a class or use [`finally`](#Re-finally)
**See also**: [RAII](#Rr-raii) **See also**: [RAII](#Re-raii)
### <a name="Rr-use-ptr"></a>R.2: In interfaces, use raw pointers to denote individual objects (only) ### <a name="Rr-use-ptr"></a>R.2: In interfaces, use raw pointers to denote individual objects (only)
@@ -9283,26 +9296,7 @@ Instead, use a local variable:
### <a name="Rr-global"></a>R.6: Avoid non-`const` global variables ### <a name="Rr-global"></a>R.6: Avoid non-`const` global variables
##### Reason See [I.2](#Ri-global)
Global variables can be accessed from everywhere so they can introduce surprising dependencies between apparently unrelated objects.
They are a notable source of errors.
**Warning**: The initialization of global objects is not totally ordered.
If you use a global object initialize it with a constant.
Note that it is possible to get undefined initialization order even for `const` objects.
##### Exception
A global object is often better than a singleton.
##### Exception
An immutable (`const`) global does not introduce the problems we try to avoid by banning global objects.
##### Enforcement
(??? NM: Obviously we can warn about non-`const` statics ... do we want to?)
## <a name="SS-alloc"></a>R.alloc: Allocation and deallocation ## <a name="SS-alloc"></a>R.alloc: Allocation and deallocation
@@ -9849,8 +9843,8 @@ The following should not pass code review:
void my_code() void my_code()
{ {
// BAD: passing pointer or reference obtained from a nonlocal smart pointer // BAD: passing pointer or reference obtained from a non-local smart pointer
// that could be inadvertently reset somewhere inside f or it callees // that could be inadvertently reset somewhere inside f or its callees
f(*g_p); f(*g_p);
// BAD: same reason, just passing it as a "this" pointer // BAD: same reason, just passing it as a "this" pointer
@@ -9873,7 +9867,7 @@ The fix is simple -- take a local copy of the pointer to "keep a ref count" for
##### Enforcement ##### Enforcement
* (Simple) Warn if a pointer or reference obtained from a smart pointer variable (`Unique_pointer` or `Shared_pointer`) that is nonlocal, or that is local but potentially aliased, is used in a function call. If the smart pointer is a `Shared_pointer` then suggest taking a local copy of the smart pointer and obtain a pointer or reference from that instead. * (Simple) Warn if a pointer or reference obtained from a smart pointer variable (`Unique_pointer` or `Shared_pointer`) that is non-local, or that is local but potentially aliased, is used in a function call. If the smart pointer is a `Shared_pointer` then suggest taking a local copy of the smart pointer and obtain a pointer or reference from that instead.
# <a name="S-expr"></a>ES: Expressions and statements # <a name="S-expr"></a>ES: Expressions and statements
@@ -9890,7 +9884,7 @@ Declaration rules:
* [ES.5: Keep scopes small](#Res-scope) * [ES.5: Keep scopes small](#Res-scope)
* [ES.6: Declare names in for-statement initializers and conditions to limit scope](#Res-cond) * [ES.6: Declare names in for-statement initializers and conditions to limit scope](#Res-cond)
* [ES.7: Keep common and local names short, and keep uncommon and nonlocal names longer](#Res-name-length) * [ES.7: Keep common and local names short, and keep uncommon and non-local names longer](#Res-name-length)
* [ES.8: Avoid similar-looking names](#Res-name-similar) * [ES.8: Avoid similar-looking names](#Res-name-similar)
* [ES.9: Avoid `ALL_CAPS` names](#Res-not-CAPS) * [ES.9: Avoid `ALL_CAPS` names](#Res-not-CAPS)
* [ES.10: Declare one name (only) per declaration](#Res-name-one) * [ES.10: Declare one name (only) per declaration](#Res-name-one)
@@ -10142,7 +10136,7 @@ Note: C++17 and C++20 also add `if`, `switch`, and range-`for` initializer state
### <a name="Res-name-length"></a>ES.7: Keep common and local names short, and keep uncommon and nonlocal 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
@@ -10951,7 +10945,7 @@ The definition of `a2` is C but not C++ and is considered a security risk
##### Reason ##### Reason
It nicely encapsulates local initialization, including cleaning up scratch variables needed only for the initialization, without needing to create a needless nonlocal yet nonreusable function. It also works for variables that should be `const` but only after some initialization work. It nicely encapsulates local initialization, including cleaning up scratch variables needed only for the initialization, without needing to create a needless non-local yet non-reusable function. It also works for variables that should be `const` but only after some initialization work.
##### Example, bad ##### Example, bad
@@ -11713,7 +11707,7 @@ Casts are widely (mis) used. Modern C++ has rules and constructs that eliminate
##### Enforcement ##### Enforcement
* Force the elimination of C-style casts, except on a function with a `[[nodiscard]]` return. * Force the elimination of C-style casts, except when casting a `[[nodiscard]]` function return value to `void`.
* Warn if there are many functional style casts (there is an obvious problem in quantifying 'many'). * Warn if there are many functional style casts (there is an obvious problem in quantifying 'many').
* The [type profile](#Pro-type-reinterpretcast) bans `reinterpret_cast`. * The [type profile](#Pro-type-reinterpretcast) bans `reinterpret_cast`.
* Warn against [identity casts](#Pro-type-identitycast) between pointer types, where the source and target types are the same (#Pro-type-identitycast). * Warn against [identity casts](#Pro-type-identitycast) between pointer types, where the source and target types are the same (#Pro-type-identitycast).
@@ -11758,7 +11752,7 @@ The C-style cast is dangerous because it can do any kind of conversion, deprivin
##### Note ##### Note
When converting between types with no information loss (e.g. from `float` to When converting between types with no information loss (e.g. from `float` to
`double` or `int64` from `int32`), brace initialization may be used instead. `double` or from `int32` to `int64`), brace initialization may be used instead.
double d {some_float}; double d {some_float};
int64_t i {some_int32}; int64_t i {some_int32};
@@ -11786,7 +11780,7 @@ for example.)
##### Reason ##### Reason
It makes a lie out of `const`. It makes a lie out of `const`.
If the variable is actually declared `const`, the result of "casting away `const`" is undefined behavior. If the variable is actually declared `const`, modifying it results in undefined behavior.
##### Example, bad ##### Example, bad
@@ -12314,7 +12308,7 @@ wrong results, or memory corruption.
This rule is an obvious and well-known language rule, but can be hard to follow. This rule is an obvious and well-known language rule, but can be hard to follow.
It takes good coding style, library support, and static analysis to eliminate violations without major overhead. It takes good coding style, library support, and static analysis to eliminate violations without major overhead.
This is a major part of the discussion of [C++'s resource- and type-safety model](#Stroustrup15). This is a major part of the discussion of [C++'s model for type- and resource-safety](#Stroustrup15).
**See also**: **See also**:
@@ -12322,7 +12316,7 @@ This is a major part of the discussion of [C++'s resource- and type-safety model
* Use [unique_ptr](#Rf-unique_ptr) to avoid lifetime problems. * Use [unique_ptr](#Rf-unique_ptr) to avoid lifetime problems.
* Use [shared_ptr](#Rf-shared_ptr) to avoid lifetime problems. * Use [shared_ptr](#Rf-shared_ptr) to avoid lifetime problems.
* Use [references](#Rf-ptr-ref) when `nullptr` isn't a possibility. * Use [references](#Rf-ptr-ref) when `nullptr` isn't a possibility.
* Use [not_null](#Rf-not_null) to catch unexpected `nullptr` early. * Use [not_null](#Rf-nullptr) to catch unexpected `nullptr` early.
* Use the [bounds profile](#SS-bounds) to avoid range errors. * Use the [bounds profile](#SS-bounds) to avoid range errors.
@@ -12702,17 +12696,62 @@ consider `gsl::finally()` as a cleaner and more reliable alternative to `goto ex
##### Example ##### Example
??? switch(x){
case 1 :
while(/* some condition */){
//...
break;
} //Oops! break switch or break while intended?
case 2 :
//...
break;
}
##### Alternative ##### Alternative
Often, a loop that requires a `break` is a good candidate for a function (algorithm), in which case the `break` becomes a `return`. Often, a loop that requires a `break` is a good candidate for a function (algorithm), in which case the `break` becomes a `return`.
??? //Original code: break inside loop
void use1(){
std::vector<T> vec = {/* initialized with some values */};
T value;
for(const T item : vec){
if(/* some condition*/){
value = item;
break;
}
}
/* then do something with value */
}
//BETTER: create a function and return inside loop
T search(const std::vector<T> &vec){
for(const T &item : vec){
if(/* some condition*/) return item;
}
return T(); //default value
}
void use2(){
std::vector<T> vec = {/* initialized with some values */};
T value = search(vec);
/* then do something with value */
}
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.
??? for(int item : vec){ //BAD
if(item%2 == 0) continue;
if(item == 5) continue;
if(item > 10) continue;
/* do something with item */
}
for(int item : vec){ //GOOD
if(item%2 != 0 && item != 5 && item <= 10){
/* do something with item */
}
}
##### Note ##### Note
@@ -14924,8 +14963,30 @@ There is no explicit locking and both correct (value) return and error (exceptio
##### Example ##### Example
??? int read_value(const std::string& filename)
{
std::ifstream in(filename);
in.exceptions(std::ifstream::failbit);
int value;
in >> value;
return value;
}
void async_example()
{
try
{
auto v1 = std::async(std::launch::async, read_value, "v1.txt");
auto v2 = std::async(std::launch::async, read_value, "v2.txt");
std::cout << v1.get() + v2.get() << '\n';
}
catch (std::ios_base::failure & fail)
{
// handle exception here
}
}
##### Note ##### Note
Unfortunately, `async()` is not perfect. Unfortunately, `async()` is not perfect.
@@ -15517,7 +15578,7 @@ One strategy is to add a `valid()` operation to every resource handle:
Obviously, this increases the size of the code, doesn't allow for implicit propagation of "exceptions" (`valid()` checks), and `valid()` checks can be forgotten. Obviously, this increases the size of the code, doesn't allow for implicit propagation of "exceptions" (`valid()` checks), and `valid()` checks can be forgotten.
Prefer to use exceptions. Prefer to use exceptions.
**See also**: [Use of `noexcept`](#Se-noexcept) **See also**: [Use of `noexcept`](#Re-noexcept)
##### Enforcement ##### Enforcement
@@ -16445,7 +16506,7 @@ Templates can also be used for meta-programming; that is, programs that compose
A central notion in generic programming is "concepts"; that is, requirements on template arguments presented as compile-time predicates. A central notion in generic programming is "concepts"; that is, requirements on template arguments presented as compile-time predicates.
"Concepts" are defined in an ISO Technical Specification: [concepts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf). "Concepts" are defined in an ISO Technical Specification: [concepts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf).
A draft of a set of standard-library concepts can be found in another ISO TS: [ranges](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf) A draft of a set of standard-library concepts can be found in another ISO TS: [ranges](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf).
Concepts are supported in GCC 6.1 and later. Concepts are supported in GCC 6.1 and later.
Consequently, we comment out uses of concepts in examples; that is, we use them as formalized comments only. Consequently, we comment out uses of concepts in examples; that is, we use them as formalized comments only.
If you use GCC 6.1 or later, you can uncomment them. If you use GCC 6.1 or later, you can uncomment them.
@@ -16475,8 +16536,8 @@ Concept definition rule summary:
* [T.24: Use tag classes or traits to differentiate concepts that differ only in semantics](#Rt-tag) * [T.24: Use tag classes or traits to differentiate concepts that differ only in semantics](#Rt-tag)
* [T.25: Avoid complementary constraints](#Rt-not) * [T.25: Avoid complementary constraints](#Rt-not)
* [T.26: Prefer to define concepts in terms of use-patterns rather than simple syntax](#Rt-use) * [T.26: Prefer to define concepts in terms of use-patterns rather than simple syntax](#Rt-use)
* [T.30: Use concept negation (`!C<T>`) sparingly to express a minor difference](#Rt-not) * [T.30: Use concept negation (`!C<T>`) sparingly to express a minor difference](#Rt-???)
* [T.31: Use concept disjunction (`C1<T> || C2<T>`) sparingly to express alternatives](#Rt-or) * [T.31: Use concept disjunction (`C1<T> || C2<T>`) sparingly to express alternatives](#Rt-???)
* ??? * ???
Template interface rule summary: Template interface rule summary:
@@ -16500,7 +16561,7 @@ Template definition rule summary:
* [T.65: Use tag dispatch to provide alternative implementations of functions](#Rt-tag-dispatch) * [T.65: Use tag dispatch to provide alternative implementations of functions](#Rt-tag-dispatch)
* [T.67: Use specialization to provide alternative implementations for irregular types](#Rt-specialization2) * [T.67: Use specialization to provide alternative implementations for irregular types](#Rt-specialization2)
* [T.68: Use `{}` rather than `()` within templates to avoid ambiguities](#Rt-cast) * [T.68: Use `{}` rather than `()` within templates to avoid ambiguities](#Rt-cast)
* [T.69: Inside a template, don't make an unqualified nonmember function call unless you intend it to be a customization point](#Rt-customization) * [T.69: Inside a template, don't make an unqualified non-member function call unless you intend it to be a customization point](#Rt-customization)
Template and hierarchy rule summary: Template and hierarchy rule summary:
@@ -16534,7 +16595,7 @@ Other template rules summary:
* [T.140: Name all operations with potential for reuse](#Rt-name) * [T.140: Name all operations with potential for reuse](#Rt-name)
* [T.141: Use an unnamed lambda if you need a simple function object in one place only](#Rt-lambda) * [T.141: Use an unnamed lambda if you need a simple function object in one place only](#Rt-lambda)
* [T.142: Use template variables to simplify notation](#Rt-var) * [T.142: Use template variables to simplify notation](#Rt-var)
* [T.143: Don't write unintentionally nongeneric code](#Rt-nongeneric) * [T.143: Don't write unintentionally non-generic code](#Rt-non-generic)
* [T.144: Don't specialize function templates](#Rt-specialize-function) * [T.144: Don't specialize function templates](#Rt-specialize-function)
* [T.150: Check that a class matches a concept using `static_assert`](#Rt-check-class) * [T.150: Check that a class matches a concept using `static_assert`](#Rt-check-class)
* [T.??: ????](#Rt-???) * [T.??: ????](#Rt-???)
@@ -16720,7 +16781,7 @@ Examples include type erasure as with `std::shared_ptr`'s deleter (but [don't ov
##### Note ##### Note
In a class template, nonvirtual functions are only instantiated if they're used -- but virtual functions are instantiated every time. In a class template, non-virtual functions are only instantiated if they're used -- but virtual functions are instantiated every time.
This can bloat code size, and may overconstrain a generic type by instantiating functionality that is never needed. This can bloat code size, and may overconstrain a generic type by instantiating functionality that is never needed.
Avoid this, even though the standard-library facets made this mistake. Avoid this, even though the standard-library facets made this mistake.
@@ -16737,7 +16798,7 @@ See the reference to more specific rules.
## <a name="SS-concepts"></a>T.concepts: Concept rules ## <a name="SS-concepts"></a>T.concepts: Concept rules
Concepts is a facility for specifying requirements for template arguments. Concepts is a facility for specifying requirements for template arguments.
It is an [ISO Technical Specification](#Ref-conceptsTS), but currently supported only by GCC. It is an [ISO Technical Specification](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf), but currently supported only by GCC.
Concepts are, however, crucial in the thinking about generic programming and the basis of much work on future C++ libraries Concepts are, however, crucial in the thinking about generic programming and the basis of much work on future C++ libraries
(standard and other). (standard and other).
@@ -16824,7 +16885,7 @@ Flag template type arguments without concepts
##### Reason ##### Reason
"Standard" concepts (as provided by the [GSL](#S-GSL) and the [Ranges TS](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf), and hopefully soon the ISO standard itself) "Standard" concepts (as provided by the [GSL](#S-gsl) and the [Ranges TS](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf), and hopefully soon the ISO standard itself)
save us the work of thinking up our own concepts, are better thought out than we can manage to do in a hurry, and improve interoperability. save us the work of thinking up our own concepts, are better thought out than we can manage to do in a hurry, and improve interoperability.
##### Note ##### Note
@@ -17621,7 +17682,7 @@ Because that's the best we can do without direct concept support.
##### Note ##### Note
Beware of [complementary constraints](# T.25). Beware of [complementary constraints](#Rt-not).
Faking concept overloading using `enable_if` sometimes forces us to use that error-prone design technique. Faking concept overloading using `enable_if` sometimes forces us to use that error-prone design technique.
##### Enforcement ##### Enforcement
@@ -17687,7 +17748,7 @@ Templates typically appear in header files so their context dependencies are mor
##### Note ##### Note
Having a template operate only on its arguments would be one way of reducing the number of dependencies to a minimum, but that would generally be unmanageable. Having a template operate only on its arguments would be one way of reducing the number of dependencies to a minimum, but that would generally be unmanageable.
For example, an algorithm usually uses other algorithms and invoke operations that does not exclusively operate on arguments. For example, algorithms usually use other algorithms and invoke operations that do not exclusively operate on arguments.
And don't get us started on macros! And don't get us started on macros!
**See also**: [T.69](#Rt-customization) **See also**: [T.69](#Rt-customization)
@@ -17854,7 +17915,7 @@ This is a simplified version of `std::copy` (ignoring the possibility of non-con
// use loop calling copy constructors // use loop calling copy constructors
} }
template<class Itert> template<class Iter>
Out copy(Iter first, Iter last, Iter out) Out copy(Iter first, Iter last, Iter out)
{ {
return copy_helper(first, last, out, typename copy_trait<Iter>::tag{}) return copy_helper(first, last, out, typename copy_trait<Iter>::tag{})
@@ -17928,7 +17989,7 @@ When `concept`s become widely available such alternatives can be distinguished d
* flag function-style casts * flag function-style casts
### <a name="Rt-customization"></a>T.69: Inside a template, don't make an unqualified nonmember function call unless you intend it to be a customization point ### <a name="Rt-customization"></a>T.69: Inside a template, don't make an unqualified non-member function call unless you intend it to be a customization point
##### Reason ##### Reason
@@ -17948,7 +18009,7 @@ There are three major ways to let calling code customize a template.
template<class T> template<class T>
void test2(T t) void test2(T t)
// Call a nonmember function without qualification // Call a non-member function without qualification
{ {
f(t); // require f(/*T*/) be available in caller's scope or in T's namespace f(t); // require f(/*T*/) be available in caller's scope or in T's namespace
} }
@@ -17970,12 +18031,12 @@ or a traditional traits template to be specialized on the user's type.
If you intend to call your own helper function `helper(t)` with a value `t` that depends on a template type parameter, If you intend to call your own helper function `helper(t)` with a value `t` that depends on a template type parameter,
put it in a `::detail` namespace and qualify the call as `detail::helper(t);`. put it in a `::detail` namespace and qualify the call as `detail::helper(t);`.
An unqualified call becomes a customization point where any function `helper` in the namespace of `t`'s type can be invoked; An unqualified call becomes a customization point where any function `helper` in the namespace of `t`'s type can be invoked;
this can cause problems like [unintentionally invoking unconstrained function templates](#Rt-unconstrained-adl). this can cause problems like [unintentionally invoking unconstrained function templates](#Rt-visible).
##### Enforcement ##### Enforcement
* In a template, flag an unqualified call to a nonmember function that passes a variable of dependent type when there is a nonmember function of the same name in the template's namespace. * In a template, flag an unqualified call to a non-member function that passes a variable of dependent type when there is a non-member function of the same name in the template's namespace.
## <a name="SS-temp-hier"></a>T.temp-hier: Template and hierarchy rules: ## <a name="SS-temp-hier"></a>T.temp-hier: Template and hierarchy rules:
@@ -18468,7 +18529,7 @@ Improved readability.
??? ???
### <a name="Rt-nongeneric"></a>T.143: Don't write unintentionally nongeneric code ### <a name="Rt-non-generic"></a>T.143: Don't write unintentionally non-generic code
##### Reason ##### Reason
@@ -18682,10 +18743,11 @@ Source file rule summary:
* [SF.9: Avoid cyclic dependencies among source files](#Rs-cycles) * [SF.9: Avoid cyclic dependencies among source files](#Rs-cycles)
* [SF.10: Avoid dependencies on implicitly `#include`d names](#Rs-implicit) * [SF.10: Avoid dependencies on implicitly `#include`d names](#Rs-implicit)
* [SF.11: Header files should be self-contained](#Rs-contained) * [SF.11: Header files should be self-contained](#Rs-contained)
* [SF.12: Prefer the angle bracket form of `#include` where you can and the quoted form everywhere else](#Rs-incform)
* [SF.20: Use `namespace`s to express logical structure](#Rs-namespace) * [SF.20: Use `namespace`s to express logical structure](#Rs-namespace)
* [SF.21: Don't use an unnamed (anonymous) namespace in a header](#Rs-unnamed) * [SF.21: Don't use an unnamed (anonymous) namespace in a header](#Rs-unnamed)
* [SF.22: Use an unnamed (anonymous) namespace for all internal/nonexported entities](#Rs-unnamed2) * [SF.22: Use an unnamed (anonymous) namespace for all internal/non-exported entities](#Rs-unnamed2)
### <a name="Rs-file-suffix"></a>SF.1: Use a `.cpp` suffix for code files and `.h` for interface files if your project doesn't already follow another convention ### <a name="Rs-file-suffix"></a>SF.1: Use a `.cpp` suffix for code files and `.h` for interface files if your project doesn't already follow another convention
@@ -19120,6 +19182,33 @@ A header should include all its dependencies. Be careful about using relative pa
A test should verify that the header file itself compiles or that a cpp file which only includes the header file compiles. A test should verify that the header file itself compiles or that a cpp file which only includes the header file compiles.
### <a name="Rs-incform"></a>SF.12: Prefer the angle bracket form of `#include` where you can and the quoted form everywhere else
##### Reason
The [standard](http://eel.is/c++draft/cpp.include) provides flexibility for compilers to implement
the two forms of `#include` selected using the angle (`<>`) or quoted (`""`) syntax. Vendors take
advantage of this and use different search algorithms and methods for specifying the include path.
Nevertheless, the guidance is to use the angle form when possible. This supports the fact that the
standard library headers must be included this way, is more likely to create portable code, and enables
the quoted form for other uses. For example being clear about the locality of the header relative
to files that includes it or in scenarios where the different search algorithm is required.
##### Example
#include <string> // From the standard library, required form
#include "helpers.h" // A project specific file, use "" form
##### Note
Failing to follow this results in difficult to diagnose errors due to picking up the wrong file by incorrectly specifying the scope when it is included.
Library creators should put their headers in a folder and have clients include those files using the relative path `#include <some_library/common.h>`
##### Enforcement
A test should identify headers referenced via `""` could be referenced with `<>`.
### <a name="Rs-namespace"></a>SF.20: Use `namespace`s to express logical structure ### <a name="Rs-namespace"></a>SF.20: Use `namespace`s to express logical structure
##### Reason ##### Reason
@@ -19148,7 +19237,7 @@ It is almost always a bug to mention an unnamed namespace in a header file.
* Flag any use of an anonymous namespace in a header file. * Flag any use of an anonymous namespace in a header file.
### <a name="Rs-unnamed2"></a>SF.22: Use an unnamed (anonymous) namespace for all internal/nonexported entities ### <a name="Rs-unnamed2"></a>SF.22: Use an unnamed (anonymous) namespace for all internal/non-exported entities
##### Reason ##### Reason
@@ -20182,17 +20271,20 @@ and errors (when we didn't deal correctly with semi-constructed objects consiste
##### Example, bad ##### Example, bad
// Old conventional style: many problems
class Picture class Picture
{ {
int mx; int mx;
int my; int my;
char * data; char * data;
public: public:
// main problem: constructor does not fully construct
Picture(int x, int y) Picture(int x, int y)
{ {
mx = x, mx = x; // also bad: assignment in constructor body rather than in member initializer
my = y; my = y;
data = nullptr; data = nullptr; // also bad: constant initialization in constructor rather than in member initializer
} }
~Picture() ~Picture()
@@ -20200,6 +20292,7 @@ and errors (when we didn't deal correctly with semi-constructed objects consiste
Cleanup(); Cleanup();
} }
// bad: two-phase initialization
bool Init() bool Init()
{ {
// invariant checks // invariant checks
@@ -20209,10 +20302,11 @@ and errors (when we didn't deal correctly with semi-constructed objects consiste
if (data) { if (data) {
return false; return false;
} }
data = (char*) malloc(mx*my*sizeof(int)); data = (char*) malloc(mx*my*sizeof(int)); // also bad: owning raw * and malloc
return data != nullptr; return data != nullptr;
} }
// also bad: no reason to make cleanup a separate function
void Cleanup() void Cleanup()
{ {
if (data) free(data); if (data) free(data);
@@ -20231,20 +20325,20 @@ and errors (when we didn't deal correctly with semi-constructed objects consiste
class Picture class Picture
{ {
size_t mx; int mx;
size_t my; int my;
vector<char> data; vector<char> data;
static size_t check_size(size_t s) static int check_size(int size)
{ {
// invariant check // invariant check
Expects(s > 0); Expects(size > 0);
return s; return size;
} }
public: public:
// even more better would be a class for a 2D Size as one single parameter // even better would be a class for a 2D Size as one single parameter
Picture(size_t x, size_t y) Picture(int x, int y)
: mx(check_size(x)) : mx(check_size(x))
, my(check_size(y)) , my(check_size(y))
// now we know x and y have a valid size // now we know x and y have a valid size
@@ -20252,6 +20346,7 @@ and errors (when we didn't deal correctly with semi-constructed objects consiste
{ {
// picture is ready-to-use // picture is ready-to-use
} }
// compiler generated dtor does the job. (also see C.21) // compiler generated dtor does the job. (also see C.21)
}; };
@@ -20351,6 +20446,7 @@ Reference sections:
## <a name="SS-rules"></a>RF.rules: Coding rules ## <a name="SS-rules"></a>RF.rules: Coding rules
* [AUTOSAR Guidelines for the use of the C++14 language in critical and safety-related systems v17.10](https://www.autosar.org/fileadmin/user_upload/standards/adaptive/17-10/AUTOSAR_RS_CPP14Guidelines.pdf)
* [Boost Library Requirements and Guidelines](http://www.boost.org/development/requirements.html). * [Boost Library Requirements and Guidelines](http://www.boost.org/development/requirements.html).
???. ???.
* [Bloomberg: BDE C++ Coding](https://github.com/bloomberg/bde/wiki/CodingStandards.pdf). * [Bloomberg: BDE C++ Coding](https://github.com/bloomberg/bde/wiki/CodingStandards.pdf).
@@ -20370,6 +20466,7 @@ Reference sections:
Libraries used have to have been approved for mission critical applications. Libraries used have to have been approved for mission critical applications.
Any similarities to this set of guidelines are unsurprising because Bjarne Stroustrup was an author of JSF++. Any similarities to this set of guidelines are unsurprising because Bjarne Stroustrup was an author of JSF++.
Recommended, but note its very specific focus. Recommended, but note its very specific focus.
* [_MISRA C++ 2008: Guidelines for the use of the C++ language in critical systems_] (https://www.misra.org.uk/Buyonline/tabid/58/Default.aspx).
* [Mozilla Portability Guide](https://developer.mozilla.org/en-US/docs/Mozilla/C%2B%2B_Portability_Guide). * [Mozilla Portability Guide](https://developer.mozilla.org/en-US/docs/Mozilla/C%2B%2B_Portability_Guide).
As the name indicates, this aims for portability across many (old) compilers. As the name indicates, this aims for portability across many (old) compilers.
As such, it is restrictive. As such, it is restrictive.
@@ -20382,7 +20479,7 @@ Reference sections:
Many of their rules apply generally. Many of their rules apply generally.
* [High Integrity C++ Coding Standard](http://www.codingstandard.com/). * [High Integrity C++ Coding Standard](http://www.codingstandard.com/).
* [llvm](http://llvm.org/docs/CodingStandards.html). * [llvm](http://llvm.org/docs/CodingStandards.html).
Somewhat brief, pre-C++11, and (not unreasonably) adjusted to its domain. Somewhat brief, based on C++14, and (not unreasonably) adjusted to its domain.
* ??? * ???
## <a name="SS-books"></a>RF.books: Books with coding guidelines ## <a name="SS-books"></a>RF.books: Books with coding guidelines
@@ -20563,13 +20660,13 @@ Type safety profile summary:
* <a name="Pro-type-constcast"></a>Type.3: Don't use `const_cast` to cast away `const` (i.e., at all): * <a name="Pro-type-constcast"></a>Type.3: Don't use `const_cast` to cast away `const` (i.e., at all):
[Don't cast away const](#Res-casts-const). [Don't cast away const](#Res-casts-const).
* <a name="Pro-type-cstylecast"></a>Type.4: Don't use C-style `(T)expression` or functional `T(expression)` casts: * <a name="Pro-type-cstylecast"></a>Type.4: Don't use C-style `(T)expression` or functional `T(expression)` casts:
Prefer [construction](#Res-construct) or [named casts](#Res-cast-named). Prefer [construction](#Res-construct) or [named casts](#Res-casts-named).
* <a name="Pro-type-init"></a>Type.5: Don't use a variable before it has been initialized: * <a name="Pro-type-init"></a>Type.5: Don't use a variable before it has been initialized:
[always initialize](#Res-always). [always initialize](#Res-always).
* <a name="Pro-type-memberinit"></a>Type.6: Always initialize a member variable: * <a name="Pro-type-memberinit"></a>Type.6: Always initialize a member variable:
[always initialize](#Res-always), [always initialize](#Res-always),
possibly using [default constructors](#Rc-default0) or possibly using [default constructors](#Rc-default0) or
[default member initializers](#Rc-in-class-initializers). [default member initializers](#Rc-in-class-initializer).
* <a name="Pro-type-unon"></a>Type.7: Avoid naked union: * <a name="Pro-type-unon"></a>Type.7: Avoid naked union:
[Use `variant` instead](#Ru-naked). [Use `variant` instead](#Ru-naked).
* <a name="Pro-type-varargs"></a>Type.8: Avoid varargs: * <a name="Pro-type-varargs"></a>Type.8: Avoid varargs:
@@ -20648,9 +20745,13 @@ The GSL is header only, and can be found at [GSL: Guidelines support library](ht
The support library facilities are designed to be extremely lightweight (zero-overhead) so that they impose no overhead compared to using conventional alternatives. The support library facilities are designed to be extremely lightweight (zero-overhead) so that they impose no overhead compared to using conventional alternatives.
Where desirable, they can be "instrumented" with additional functionality (e.g., checks) for tasks such as debugging. Where desirable, they can be "instrumented" with additional functionality (e.g., checks) for tasks such as debugging.
These Guidelines assume a `variant` type, but this is not currently in GSL. These Guidelines use types from the standard (e.g., C++17) in addition to ones from the GSL.
For example, we assume a `variant` type, but this is not currently in GSL.
Eventually, use [the one voted into C++17](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0088r3.html). Eventually, use [the one voted into C++17](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0088r3.html).
Some of the GSL types listed below may not be supported in the library you use due to technical reasons such as limitations in the current versions of C++.
Therefore, please consult your GSL documentation to find out more.
Summary of GSL components: Summary of GSL components:
* [GSL.view: Views](#SS-views) * [GSL.view: Views](#SS-views)
@@ -21682,9 +21783,9 @@ In summary, no post-construction technique is perfect. The worst techniques dodg
**References**: [\[Alexandrescu01\]](#Alexandrescu01) §3, [\[Boost\]](#Boost), [\[Dewhurst03\]](#Dewhurst03) §75, [\[Meyers97\]](#Meyers97) §46, [\[Stroustrup00\]](#Stroustrup00) §15.4.3, [\[Taligent94\]](#Taligent94) **References**: [\[Alexandrescu01\]](#Alexandrescu01) §3, [\[Boost\]](#Boost), [\[Dewhurst03\]](#Dewhurst03) §75, [\[Meyers97\]](#Meyers97) §46, [\[Stroustrup00\]](#Stroustrup00) §15.4.3, [\[Taligent94\]](#Taligent94)
### <a name="Sd-dtor"></a>Discussion: Make base class destructors public and virtual, or protected and nonvirtual ### <a name="Sd-dtor"></a>Discussion: Make base class destructors public and virtual, or protected and non-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. 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 non-virtual since it doesn't need to behave virtually.
##### Example ##### Example
@@ -21704,7 +21805,7 @@ The common case for a base class is that it's intended to have publicly derived
// ... // ...
} // ~pb invokes correct destructor only when ~Base is virtual } // ~pb invokes correct destructor only when ~Base is virtual
In rarer cases, such as policy classes, the class is used as a base class for convenience, not for polymorphic behavior. It is recommended to make those destructors protected and nonvirtual: In rarer cases, such as policy classes, the class is used as a base class for convenience, not for polymorphic behavior. It is recommended to make those destructors protected and non-virtual:
class My_policy { class My_policy {
public: public:
@@ -21721,20 +21822,20 @@ In rarer cases, such as policy classes, the class is used as a base class for co
This simple guideline illustrates a subtle issue and reflects modern uses of inheritance and object-oriented design principles. This simple guideline illustrates a subtle issue and reflects modern uses of inheritance and object-oriented design principles.
For a base class `Base`, calling code might try to destroy derived objects through pointers to `Base`, such as when using a `unique_ptr<Base>`. If `Base`'s destructor is public and nonvirtual (the default), it can be accidentally called on a pointer that actually points to a derived object, in which case the behavior of the attempted deletion is undefined. This state of affairs has led older coding standards to impose a blanket requirement that all base class destructors must be virtual. This is overkill (even if it is the common case); instead, the rule should be to make base class destructors virtual if and only if they are public. For a base class `Base`, calling code might try to destroy derived objects through pointers to `Base`, such as when using a `unique_ptr<Base>`. If `Base`'s destructor is public and non-virtual (the default), it can be accidentally called on a pointer that actually points to a derived object, in which case the behavior of the attempted deletion is undefined. This state of affairs has led older coding standards to impose a blanket requirement that all base class destructors must be virtual. This is overkill (even if it is the common case); instead, the rule should be to make base class destructors virtual if and only if they are public.
To write a base class is to define an abstraction (see Items 35 through 37). Recall that for each member function participating in that abstraction, you need to decide: To write a base class is to define an abstraction (see Items 35 through 37). Recall that for each member function participating in that abstraction, you need to decide:
* Whether it should behave virtually or not. * Whether it should behave virtually or not.
* Whether it should be publicly available to all callers using a pointer to `Base` or else be a hidden internal implementation detail. * Whether it should be publicly available to all callers using a pointer to `Base` or else be a hidden internal implementation detail.
As described in Item 39, for a normal member function, the choice is between allowing it to be called via a pointer to `Base` nonvirtually (but possibly with virtual behavior if it invokes virtual functions, such as in the NVI or Template Method patterns), virtually, or not at all. The NVI pattern is a technique to avoid public virtual functions. As described in Item 39, for a normal member function, the choice is between allowing it to be called via a pointer to `Base` non-virtually (but possibly with virtual behavior if it invokes virtual functions, such as in the NVI or Template Method patterns), virtually, or not at all. The NVI pattern is a technique to avoid public virtual functions.
Destruction can be viewed as just another operation, albeit with special semantics that make nonvirtual calls dangerous or wrong. For a base class destructor, therefore, the choice is between allowing it to be called via a pointer to `Base` virtually or not at all; "nonvirtually" is not an option. Hence, a base class destructor is virtual if it can be called (i.e., is public), and nonvirtual otherwise. Destruction can be viewed as just another operation, albeit with special semantics that make non-virtual calls dangerous or wrong. For a base class destructor, therefore, the choice is between allowing it to be called via a pointer to `Base` virtually or not at all; "non-virtually" is not an option. Hence, a base class destructor is virtual if it can be called (i.e., is public), and non-virtual otherwise.
Note that the NVI pattern cannot be applied to the destructor because constructors and destructors cannot make deep virtual calls. (See Items 39 and 55.) Note that the NVI pattern cannot be applied to the destructor because constructors and destructors cannot make deep virtual calls. (See Items 39 and 55.)
Corollary: When writing a base class, always write a destructor explicitly, because the implicitly generated one is public and nonvirtual. You can always `=default` the implementation if the default body is fine and you're just writing the function to give it the proper visibility and virtuality. Corollary: When writing a base class, always write a destructor explicitly, because the implicitly generated one is public and non-virtual. You can always `=default` the implementation if the default body is fine and you're just writing the function to give it the proper visibility and virtuality.
##### Exception ##### Exception
@@ -21747,9 +21848,9 @@ Consider also this rare case:
Then, even though the destructor has to be public, there can be great pressure to not make it virtual because as the first virtual function it would incur all the run-time type overhead when the added functionality should never be needed. Then, even though the destructor has to be public, there can be great pressure to not make it virtual because as the first virtual function it would incur all the run-time type overhead when the added functionality should never be needed.
In this rare case, you could make the destructor public and nonvirtual but clearly document that further-derived objects must not be used polymorphically as `B`'s. This is what was done with `std::unary_function`. In this rare case, you could make the destructor public and non-virtual but clearly document that further-derived objects must not be used polymorphically as `B`'s. This is what was done with `std::unary_function`.
In general, however, avoid concrete base classes (see Item 35). For example, `unary_function` is a bundle-of-typedefs that was never intended to be instantiated standalone. It really makes no sense to give it a public destructor; a better design would be to follow this Item's advice and give it a protected nonvirtual destructor. In general, however, avoid concrete base classes (see Item 35). For example, `unary_function` is a bundle-of-typedefs that was never intended to be instantiated standalone. It really makes no sense to give it a public destructor; a better design would be to follow this Item's advice and give it a protected non-virtual destructor.
**References**: [\[C++CS\]](#CplusplusCS) Item 50, [\[Cargill92\]](#Cargill92) pp. 77-79, 207, [\[Cline99\]](#Cline99) §21.06, 21.12-13, [\[Henricson97\]](#Henricson97) pp. 110-114, [\[Koenig97\]](#Koenig97) Chapters 4, 11, [\[Meyers97\]](#Meyers97) §14, [\[Stroustrup00\]](#Stroustrup00) §12.4.2, [\[Sutter02\]](#Sutter02) §27, [\[Sutter04\]](#Sutter04) §18 **References**: [\[C++CS\]](#CplusplusCS) Item 50, [\[Cargill92\]](#Cargill92) pp. 77-79, 207, [\[Cline99\]](#Cline99) §21.06, 21.12-13, [\[Henricson97\]](#Henricson97) pp. 110-114, [\[Koenig97\]](#Koenig97) Chapters 4, 11, [\[Meyers97\]](#Meyers97) §14, [\[Stroustrup00\]](#Stroustrup00) §12.4.2, [\[Sutter02\]](#Sutter02) §27, [\[Sutter04\]](#Sutter04) §18
@@ -21927,7 +22028,7 @@ In many cases, holding properly encapsulated resources using RAII "owning" objec
Prefer compiler-generated (including `=default`) special members; only these can be classified as "trivial", and at least one major standard library vendor heavily optimizes for classes having trivial special members. This is likely to become common practice. Prefer compiler-generated (including `=default`) special members; only these can be classified as "trivial", and at least one major standard library vendor heavily optimizes for classes having trivial special members. This is likely to become common practice.
**Exceptions**: When any of the special functions are declared only to make them nonpublic or virtual, but without special semantics, it doesn't imply that the others are needed. **Exceptions**: When any of the special functions are declared only to make them non-public or virtual, but without special semantics, it doesn't imply that the others are needed.
In rare cases, classes that have members of strange types (such as reference members) are an exception because they have peculiar copy semantics. In rare cases, classes that have members of strange types (such as reference members) are an exception because they have peculiar copy semantics.
In a class holding a reference, you likely need to write the copy constructor and the assignment operator, but the default destructor already does the right thing. (Note that using a reference member is almost always wrong.) In a class holding a reference, you likely need to write the copy constructor and the assignment operator, but the default destructor already does the right thing. (Note that using a reference member is almost always wrong.)
@@ -22386,6 +22487,10 @@ Alternatively, we will decide that no change is needed and delete the entry.
\[Meyers96]: S. Meyers. More Effective C++ (Addison-Wesley, 1996). \[Meyers96]: S. Meyers. More Effective C++ (Addison-Wesley, 1996).
* <a name="Meyers97"></a> * <a name="Meyers97"></a>
\[Meyers97]: S. Meyers. Effective C++ (2nd Edition) (Addison-Wesley, 1997). \[Meyers97]: S. Meyers. Effective C++ (2nd Edition) (Addison-Wesley, 1997).
* <a name="Meyers01"></a>
\[Meyers01]: S. Meyers. Effective STL (Addison-Wesley, 2001).
* <a name="Meyers05"></a>
\[Meyers05]: S. Meyers. Effective C++ (3rd Edition) (Addison-Wesley, 2005).
* <a name="Meyers15"></a> * <a name="Meyers15"></a>
\[Meyers15]: S. Meyers. Effective Modern C++ (O'Reilly, 2015). \[Meyers15]: S. Meyers. Effective Modern C++ (O'Reilly, 2015).
* <a name="Murray93"></a> * <a name="Murray93"></a>