diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 85a2a67..adacc6e 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -128,7 +128,7 @@ Comments and suggestions for improvements are most welcome. We plan to modify and extend this document as our understanding improves and the language and the set of available libraries improve. -# In: Introduction +# In: Introduction This is a set of core guidelines for modern C++, C++14, and taking likely future enhancements and taking ISO Technical Specifications (TSs) into account. The aim is to help C++ programmers to write simpler, more efficient, more maintainable code. @@ -143,12 +143,12 @@ Introduction summary: * [In.sec: Major sections](#SS-sec) -## In.target: Target readership +## In.target: Target readership All C++ programmers. This includes [programmers who might consider C](#S-cpl). -## In.aims: Aims +## In.aims: Aims The purpose of this document is to help developers to adopt modern C++ (C++11, C++14, and soon C++17) and to achieve a more uniform style across code bases. @@ -261,7 +261,7 @@ The profiles are intended to be used by tools, but also serve as an aid to the h We do not limit our comment in the **Enforcement** sections to things we know how to enforce; some comments are mere wishes that might inspire some tool builder. -## In.struct: The structure of this document +## In.struct: The structure of this document Each rule (guideline, suggestion) can have several parts: @@ -292,7 +292,7 @@ It is meant to be helpful, rather than complete, fully accurate on technical det Recommended information sources can be found in [the references](#S-references). -## In.sec: Major sections +## In.sec: Major sections * [P: Philosophy](#S-philosophy) * [I: Interfaces](#S-interfaces) @@ -324,7 +324,7 @@ Supporting sections: These sections are not orthogonal. -Each section (e.g., "P" for "Philosophy") and each subsection (e.g., "C.hier" for "Class Hierachies (OOP)") have an abbreviation for ease of searching and reference. +Each section (e.g., "P" for "Philosophy") and each subsection (e.g., "C.hier" for "Class Hierarchies (OOP)") have an abbreviation for ease of searching and reference. The main section abbreviations are also used in rule numbers (e.g., "C.11" for "Make concrete types regular"). @@ -718,23 +718,23 @@ The date is validated twice (by the `Date` constructor) and passed as a characte There are cases where checking early is dumb because you may not ever need the value, or may only need part of the value that is more easily checked than the whole. - class Jet { // Physics says: e*e < x*x + y*y + z*z - float fx, fy, fz, fe; - public: - Jet(float x, float y, float z, float e) - :fx(x), fy(y), fz(z), fe(e) - { - // Should I check the here that the values are physically meaningful? - } + class Jet { // Physics says: e*e < x*x + y*y + z*z + float fx, fy, fz, fe; + public: + Jet(float x, float y, float z, float e) + :fx(x), fy(y), fz(z), fe(e) + { + // Should I check the here that the values are physically meaningful? + } - float m() const - { - // Should I handle the degenerate case here? - return sqrt(x*x + y*y + z*z - e*e); - } + float m() const + { + // Should I handle the degenerate case here? + return sqrt(x*x + y*y + z*z - e*e); + } - ??? - }; + ??? + }; The physical law for a jet (`e*e < x*x + y*y + z*z`) is not an invariant because the possibility of measurement errors. @@ -2158,7 +2158,7 @@ If the writer of `g()` makes an assumption about the size of `buffer` a bad logi **Enforcement**: Flag a function that takes a `TP&&` parameter (where `TP` is a template type parameter name) and uses it without `std::forward`. -### F.25: Use a `T&&` parameter together with `move` for rare optimization opportunities +### F.25: Use a `T&&` parameter together with `move` for rare optimization opportunities **Reason**: Moving from an object leaves an object in its moved-from state behind. In general, moved-from objects are dangerous. The only guaranteed operation is destruction (more generally, member functions without preconditions). @@ -2585,8 +2585,8 @@ Subsections: **Example**: - void draw(int x, int y, int x2, int y2); // BAD: unnecessary implicit relationships - void draw(Point from, Point to) // better + void draw(int x, int y, int x2, int y2); // BAD: unnecessary implicit relationships + void draw(Point from, Point to); // better **Note**: A simple class without virtual functions implies no space or time overhead. @@ -3024,7 +3024,7 @@ The whole purpose of `final_action` is to get a piece of code (usually a lambda) string s; int i; vector vi; - } + }; The default destructor does it better, more efficiently, and can't get it wrong. @@ -3353,7 +3353,7 @@ It is often a good idea to express the invariant as an `Ensure` on the construct struct Rec2{ string s; int i; - Rec2(const string& ss, int ii = 0} :s{ss}, i{ii} {} // redundant + Rec2(const string& ss, int ii = 0) :s{ss}, i{ii} {} // redundant }; Rec r1 {"Foo", 7}; @@ -3413,7 +3413,7 @@ The idiom of having constructors acquire resources and destructors release them // ... public: X2(const string& name) - :f{fopen(name.c_str(), "r"} + :f{fopen(name.c_str(), "r")} { if (f==nullptr) throw runtime_error{"could not open" + name}; // ... @@ -3438,7 +3438,7 @@ The idiom of having constructors acquire resources and destructors release them // ... public: X3(const string& name) - :f{fopen(name.c_str(), "r"}, valid{false} + :f{fopen(name.c_str(), "r")}, valid{false} { if (f) valid=true; // ... @@ -3451,7 +3451,7 @@ The idiom of having constructors acquire resources and destructors release them void f() { - X3 file {Heraclides"}; + X3 file {"Heraclides"}; file.read(); // crash or bad read! // ... if (is_valid()()) { @@ -3881,6 +3881,7 @@ Types can be defined to move for logical as well as performance reasons. T* elem; int sz; }; + } Vector& Vector::operator=(const Vector& a) { @@ -4049,6 +4050,7 @@ Consider: **Example**: + template class X { // OK: value semantics public: X(); @@ -4134,6 +4136,7 @@ A non-throwing move will be used more efficiently by standard-library and langua **Example**: + template class Vector { // ... Vector(Vector&& a) noexcept :elem{a.elem}, sz{a.sz} { a.sz=0; a.elem=nullptr; } @@ -4148,6 +4151,7 @@ These copy operations do not throw. **Example, bad**: + template class Vector2 { // ... Vector2(Vector2&& a) { *this = a; } // just use the copy @@ -5333,7 +5337,7 @@ Here, we ignore such cases. * [R.14: ??? array vs. pointer parameter](#Rr-ap) * [R.15: Always overload matched allocation/deallocation pairs](#Rr-pair) -* Smart pointer rule summary: +* Smart pointer rule summary: * [R.20: Use `unique_ptr` or `shared_ptr` to represent ownership](#Rr-owner) * [R.21: Prefer `unique_ptr` over `shared_ptr` unless you need to share ownership](#Rr-unique) * [R.22: Use `make_shared()` to make `shared_ptr`s](#Rr-make_shared) @@ -5348,7 +5352,7 @@ Here, we ignore such cases. * [R.36: Take a `const shared_ptr&` 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) -### Rule R.1: Manage resources automatically using resource handles and RAII (resource acquisition is initialization) +### Rule R.1: Manage resources automatically using resource handles and RAII (resource acquisition is initialization) **Reason**: To avoid leaks and the complexity of manual resource management. C++'s language-enforced constructor/destructor symmetry mirrors the symmetry inherent in resource acquire/release function pairs such as `fopen`/`fclose`, `lock`/`unlock`, and `new`/`delete`. @@ -5402,7 +5406,7 @@ What is `Port`? A handy wrapper that encapsulates the resource: **See also**: [RAII](#Rr-raii). -### R.2: In interfaces, use raw pointers to denote individual objects (only) +### R.2: In interfaces, use raw pointers to denote individual objects (only) **Reason**: Arrays are best represented by a container type (e.g., `vector` (owning)) or an `array_view` (non-owning). Such containers and views hold sufficient information to do range checking. @@ -5592,7 +5596,7 @@ They are a notable source of errors. ## R.alloc: Allocation and deallocation -### R.10: Avoid `malloc()` and `free()` +### R.10: Avoid `malloc()` and `free()` **Reason**: `malloc()` and `free()` do not support construction and destruction, and do not mix well with `new` and `delete`. @@ -5633,7 +5637,7 @@ In such cases, consider the `nothrow` versions of `new`. **Enforcement**: Flag explicit use of `malloc` and `free`. -### R.11: Avoid calling `new` and `delete` explicitly +### R.11: Avoid calling `new` and `delete` explicitly **Reason**: The pointer returned by `new` should belong to a resource handle (that can call `delete`). If the pointer returned from `new` is assigned to a plain/naked pointer, the object can be leaked. @@ -5646,7 +5650,7 @@ If you have a naked `new`, you probably need a naked `delete` somewhere, so you **Enforcement**: (Simple) Warn on any explicit use of `new` and `delete`. Suggest using `make_unique` instead. -### R.12: Immediately give the result of an explicit resource allocation to a manager object +### R.12: Immediately give the result of an explicit resource allocation to a manager object **Reason**: If you don't, an exception or a return may lead to a leak. @@ -5678,7 +5682,7 @@ The use of the file handle (in `ifstream`) is simple, efficient, and safe. * Flag explicit allocations used to initialize pointers (problem: how many direct resource allocations can we recognize?) -### R.13: Perform at most one explicit resource allocation in a single expression statement +### R.13: Perform at most one explicit resource allocation in a single expression statement **Reason**: If you perform two explicit resource allocations in one statement, you could leak resources because the order of evaluation of many subexpressions, including function arguments, is unspecified. @@ -5713,7 +5717,7 @@ Write your own factory wrapper if there is not one already. * Flag expressions with multiple explicit resource allocations (problem: how many direct resource allocations can we recognize?) -### R.14: ??? array vs. pointer parameter +### R.14: ??? array vs. pointer parameter **Reason**: An array decays to a pointer, thereby losing its size, opening the opportunity for range errors. @@ -5791,7 +5795,7 @@ This will leak the object used to initialize `p1` (only). **Enforcement**: (Simple) Warn if a function uses a `Shared_ptr` with an object allocated within the function, but never returns the `Shared_ptr` or passes it to a function requiring a `Shared_ptr&`. Suggest using `unique_ptr` instead. -### R.22: Use `make_shared()` to make `shared_ptr`s +### R.22: Use `make_shared()` to make `shared_ptr`s **Reason**: If you first make an object and then gives it to a `shared_ptr` constructor, you (most likely) do one more allocation (and later deallocation) than if you use `make_shared()` because the reference counts must be allocated separately from the object. @@ -5805,7 +5809,7 @@ The `make_shared()` version mentions `X` only once, so it is usually shorter (as **Enforcement**: (Simple) Warn if a `shared_ptr` is constructed from the result of `new` rather than `make_shared`. -### Rule R.23: Use `make_unique()` to make `unique_ptr`s +### Rule R.23: Use `make_unique()` to make `unique_ptr`s **Reason**: for convenience and consistency with `shared_ptr`. @@ -5814,7 +5818,7 @@ The `make_shared()` version mentions `X` only once, so it is usually shorter (as **Enforcement**: (Simple) Warn if a `Shared_ptr` is constructed from the result of `new` rather than `make_unique`. -### R.24: Use `std::weak_ptr` to break cycles of `shared_ptr`s +### R.24: Use `std::weak_ptr` to break cycles of `shared_ptr`s **Reason**: `shared_ptr`'s rely on use counting and the use count for a cyclic structure never goes to zero, so we need a mechanism to be able to destroy a cyclic structure. @@ -6414,8 +6418,8 @@ or better using concepts or double scalbn( // better: x*pow(FLT_RADIX, n); FLT_RADIX is usually 2 - double x; // base value - int n; // exponent + double x, // base value + int n // exponent ); or @@ -6439,7 +6443,7 @@ or auto s = v.size(); auto h = t.future(); auto q = new int[s]; - auto f = [](int x){ return x+10; } + auto f = [](int x){ return x+10; }; In each case, we save writing a longish, hard-to-remember type that the compiler already knows but a programmer could get wrong. @@ -8066,7 +8070,7 @@ One strategy is to add a `valid()` operation to every resource handle: void f() { - Vector vs(100); // not std::vector: valid() added + vector vs(100); // not std::vector: valid() added if (!vs.valid()) { // handle error or exit } @@ -8606,7 +8610,7 @@ It also avoids brittle or inefficient workarounds. Convention: That's the way th int sz; }; - Vector v(10); + vector v(10); v[7] = 9.9; **Example, bad**: @@ -9441,14 +9445,14 @@ The two language mechanisms can be use effectively in combination, but a few des // ... }; - Vector vi; - Vector vs; + vector vi; + vector vs; It is probably a dumb idea to define a `sort` as a member function of a container, but it is not unheard of and it makes a good example of what not to do. -Given this, the compiler cannot know if `Vector::sort()` is called, so it must generate code for it. -Similar for `Vector::sort()`. +Given this, the compiler cannot know if `vector::sort()` is called, so it must generate code for it. +Similar for `vector::sort()`. Unless those two functions are called that's code bloat. Imagine what this would do to a class hierarchy with dozens of member functions and dozens of derived classes with many instantiations. @@ -10708,7 +10712,7 @@ Pointers should only refer to single objects, and pointer arithmetic is fragile a[4] = 1; // OK - a[count – 1] = 2; // OK + a[count - 1] = 2; // OK use(a.data(), 3); // OK } @@ -10798,8 +10802,8 @@ Issue a diagnostic for any indexing expression on an expression or variable of a void f(int i, int j) { - a[i + j] = 12; // BAD, could be rewritten as... - at(a, i + j) = 12; // OK - bounds-checked + a[i + j] = 12; // BAD, could be rewritten as... + at(a, i + j) = 12; // OK - bounds-checked } @@ -11722,7 +11726,7 @@ When using exceptions as your error handling mechanism, always document this beh -## Define Copy, move, and destroy consistently +## Define Copy, move, and destroy consistently **Reason**: ??? @@ -11966,7 +11970,7 @@ Now `Named` has a default constructor, a destructor, and efficient copy and move template class Vector { public: - Vector); + vector>; // ... };