diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index e03bee8..7834f1c 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -104,7 +104,7 @@ And it will run fast - you can afford to do things right. We are less concerned with low-level issues, such as naming conventions and indentation style. However, no topic that can help a programmer is out of bounds. -Our initial set of rules emphasize safety (of various forms) and simplicity. +Our initial set of rules emphasizes safety (of various forms) and simplicity. They may very well be too strict. We expect to have to introduce more exceptions to better accommodate real-world needs. We also need more rules. @@ -158,7 +158,7 @@ However, we do believe that a program that uses a rule is less error-prone and m Often, rules also lead to faster/easier initial development. As far as we can tell, these rules lead to code that performs as well or better than older, more conventional techniques; they are meant to follow the zero-overhead principle -("what you don't use, you don't pay for" or "When you use an abstraction mechanism appropriately, +("what you don't use, you don't pay for" or "when you use an abstraction mechanism appropriately, you get at least as good performance as if you had handcoded using lower-level language constructs"). Consider these rules ideals for new code, opportunities to exploit when working on older code, and try to approximate these ideas as closely as feasible. Remember: @@ -471,11 +471,11 @@ A programmer should be familiar with * [The ISO C++ standard library](#S-stdlib) * Whatever foundation libraries are used for the current project(s) -**Note**: Alternative formulation: Say what should be done, rather than just how it should be done +**Note**: Alternative formulation: Say what should be done, rather than just how it should be done. **Note**: Some language constructs express intent better than others. -**Example**: if two `int`s are meant to be the coordinates of a 2D point, say so: +**Example**: If two `int`s are meant to be the coordinates of a 2D point, say so: drawline(int, int, int, int); // obscure drawline(Point, Point); // clearer @@ -724,7 +724,7 @@ or may only need part of the value that is more easily checked than the whole. 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? + // Should I check here that the values are physically meaningful? } float m() const @@ -782,7 +782,7 @@ Prefer [RAII](#Rr-raii): Where feasible, replace owners with standard-library resource handles (as in the example above). Alternatively, mark an owner as such using `owner` from [the GSL](#S-gsl). * Look for naked `new` and `delete` -* look for known resource allocating functions returning raw pointers (such as `fopen`, `malloc`, and `strdup`) +* Look for known resource allocating functions returning raw pointers (such as `fopen`, `malloc`, and `strdup`) ### P.9: Don't waste time or space @@ -808,8 +808,8 @@ Prefer [RAII](#Rr-raii): if (p==nullptr) throw Nullptr_error{}; int n = strlen(p); auto buf = new char[n]; - for (int i = 0; i I.4: Make interfaces precisely and strongly typed Reason: Types are the simplest and best documentation, have well-defined meaning, and are guaranteed to be checked at compile time. -Also, precisely typed code often optimize better. +Also, precisely typed code is often optimized better. **Example; don't**: Consider @@ -1025,8 +1025,8 @@ Obviously, we cannot catch all errors through the static type system **Enforcement**: -* (Simple) Report the use of void* as a parameter or return type. -* (Hard to do well) Look for member functions with many built-in type arguments +* (Simple) Report the use of `void*` as a parameter or return type. +* (Hard to do well) Look for member functions with many built-in type arguments. ### I.5: State preconditions (if any) @@ -1058,7 +1058,7 @@ We don't need to mention it for each member function. **Enforcement**: (Not enforceable) -**See also**: the rules for passing pointers. +**See also**: The rules for passing pointers. ### I.6: Prefer `Expects()` for expressing preconditions @@ -1080,7 +1080,7 @@ We don't need to mention it for each member function. **Note**: `Expects()` can also be used to check a condition in the middle of an algorithm. -**Enforcement**: (Not enforceable) Finding the variety of ways preconditions can be asserted is not feasible. Warning about those that can be easily identified (assert()) has questionable value in the absence of a language facility. +**Enforcement**: (Not enforceable) Finding the variety of ways preconditions can be asserted is not feasible. Warning about those that can be easily identified (`assert()`) has questionable value in the absence of a language facility. ### I.7: State postconditions @@ -1122,7 +1122,7 @@ There was no postcondition stating that the buffer should be cleared and the opt Ensures(buffer[0]==0); } -**Note** postconditions are often informally stated in a comment that states the purpose of a function; `Ensures()` can be used to make this more systematic, visible, and checkable. +**Note**: Postconditions are often informally stated in a comment that states the purpose of a function; `Ensures()` can be used to make this more systematic, visible, and checkable. **Note**: Postconditions are especially important when they relate to something that is not directly reflected in a returned result, such as a state of a data structure used. @@ -1175,13 +1175,13 @@ Postconditions related only to internal state belongs in the definition/implemen Ensures(buffer[0]==0); } -**Note**: postconditions can be stated in many ways, including comments, `if`-statements, and `assert()`. This can make them hard to distinguish from ordinary code, hard to update, hard to manipulate by tools, and may have the wrong semantics. +**Note**: Postconditions can be stated in many ways, including comments, `if`-statements, and `assert()`. This can make them hard to distinguish from ordinary code, hard to update, hard to manipulate by tools, and may have the wrong semantics. **Alternative**: Postconditions of the form "this resource must be released" are best expressed by [RAII](#Rr-raii). -Ideally, that `Ensures` should be part of the interface that's not easily done. For now, we place it in the definition (function body). +Ideally, that `Ensures` should be part of the interface, but that's not easily done. For now, we place it in the definition (function body). -**Enforcement**: (Not enforceable) Finding the variety of ways postconditions can be asserted is not feasible. Warning about those that can be easily identified (assert()) has questionable value in the absence of a language facility. +**Enforcement**: (Not enforceable) Finding the variety of ways postconditions can be asserted is not feasible. Warning about those that can be easily identified (`assert()`) has questionable value in the absence of a language facility. ### I.9: If an interface is a template, document its parameters using concepts @@ -1201,7 +1201,7 @@ Ideally, that `Ensures` should be part of the interface that's not easily done. **See also**: See [generic programming](???) and [???](???) -**Enforcement**: (Not enforceable yet) A language facility is under specification. When the language facility is available, warn if any non-variadic template parameter is not constrained by a concept (in its declaration or mentioned in a `requires` clause. +**Enforcement**: (Not enforceable yet) A language facility is under specification. When the language facility is available, warn if any non-variadic template parameter is not constrained by a concept (in its declaration or mentioned in a `requires` clause). ### I.10: Use exceptions to signal a failure to perform a required task @@ -1224,7 +1224,7 @@ For example, not being able to connect to a remote server is not by itself an er the server can refuse a connection for all kinds of reasons, so the natural thing is to return a result that the caller always has to check. However, if failing to make a connection is considered an error, then a failure should throw an exception. -**Exception**: Many traditional interface functions (e.g., UNIX signal handlers) use error codes (e.g., `errno`) to report what are really status codes, rather than errors. You don't have good alternative to using such, so calling these does not violate the rule. +**Exception**: Many traditional interface functions (e.g., UNIX signal handlers) use error codes (e.g., `errno`) to report what are really status codes, rather than errors. You don't have a good alternative to using such, so calling these does not violate the rule. **Alternative**: If you can't use exceptions (e.g. because your code is full of old-style raw-pointer use or because there are hard-real-time constraints), consider using a style that returns a pair of values: @@ -1249,7 +1249,7 @@ consider using a style that returns a pair of values: **Enforcement**: * (Not enforceable) This is a philosophical guideline that is infeasible to check directly. -* look for `errno`. +* Look for `errno`. ### I.11: Never transfer ownership by a raw pointer (`T*`) @@ -1279,7 +1279,7 @@ Consider returning the result by value (use move semantics if the result is larg However that is less elegant and less efficient unless reference semantics are needed. **Alternative**: Sometimes older code can't be modified because of ABI compatibility requirements or lack of resources. -In that case, mark owning pointers using `owner` : +In that case, mark owning pointers using `owner`: owner compute(args) // It is now clear that ownership is transferred { @@ -1435,9 +1435,13 @@ Use `const` for the "from" argument: void copy_n(const T* p, T* q, int n); // copy from [p:p+n) to [q:q+n) +**Example**: If the order of the parameters is not important, there is no problem: + + int max(int a, int b); + **Alternative**: Don't pass arrays as pointers, pass an object representing a range (e.g., an `array_view`): - void copy_n(array_view p, array_view q); // copy from b to q + void copy_n(array_view p, array_view q); // copy from p to q **Enforcement**: (Simple) Warn if two consecutive parameters share the same type. @@ -1480,9 +1484,9 @@ This will force every derived class to compute a center -- even if that's non-tr **Exception**: You can carefully craft an interface using a few carefully selected higher-level C++ types. See ???. -**Exception**: Common ABIs are emerging on some platforms freeing you from the more Draconian restrictions. +**Exception**: Common ABIs are emerging on some platforms freeing you from the more draconian restrictions. -**Note**: if you use a single compiler, you can use full C++ in interfaces. That may require recompilation after an upgrade to a new compiler version. +**Note**: If you use a single compiler, you can use full C++ in interfaces. That may require recompilation after an upgrade to a new compiler version. **Enforcement**: (Not enforceable) It is difficult to reliably identify where an interface forms part of an ABI. @@ -1504,7 +1508,7 @@ Function definition rules: * [F.4: If a function may have to be evaluated at compile time, declare it `constexpr`](#Rf-constexpr) * [F.5: If a function is very small and time critical, declare it inline](#Rf-inline) * [F.6: If your function may not throw, declare it `noexcept`](#Rf-noexcept) -* [F.7: For general use, take `T*` arguments rather than a smart pointers](#Rf-smart) +* [F.7: For general use, take `T*` arguments rather than smart pointers](#Rf-smart) * [F.8: Prefer pure functions](#Rf-pure) Argument passing rules: @@ -1550,7 +1554,7 @@ A function definition is a function declaration that also specifies the function ### F.1: "Package" meaningful operations as carefully named functions **Reason**: Factoring out common code makes code more readable, more likely to be reused, and limit errors from complex code. -If something is a well-specified action, separate it out from its surrounding code and give it a name. +If something is a well-specified action, separate it out from its surrounding code and give it a name. **Example, don't**: @@ -1564,7 +1568,7 @@ If something is a well-specified action, separate it out from its surrounding c } Almost everything is wrong with `read_and_print`. -It reads, it writes (to a fixed `ostream`), it write error messages (to a fixed `ostream`), it handles only `int`s. +It reads, it writes (to a fixed `ostream`), it writes error messages (to a fixed `ostream`), it handles only `int`s. There is nothing to reuse, logically separate operations are intermingled and local variables are in scope after the end of their logical use. For a tiny example, this looks OK, but if the input operation, the output operation, and the error handling had been more complicated the tangled mess could become hard to understand. @@ -1585,7 +1589,7 @@ Naming that lambda breaks up the expression into its logical parts and provides The shortest code is not always the best for performance or maintainability. -**Exception**: Loop bodies, including lambdas used as loop bodies, rarely needs to be named. +**Exception**: Loop bodies, including lambdas used as loop bodies, rarely need to be named. However, large loop bodies (e.g., dozens of lines or dozens of pages) can be a problem. The rule [Keep functions short](#Rf-single) implies "Keep loop bodies short." Similarly, lambdas used as callback arguments are sometimes non-trivial, yet unlikely to be re-usable. @@ -1639,7 +1643,7 @@ If there was a need, we could further templatize `read()` and `print()` on the d { input >> value; // check for errors - } + }; auto print(auto& output, const auto& value) { @@ -1736,11 +1740,11 @@ Small simple functions are easily inlined where the cost of a function call is s constexpr int max_exp = 17; // constexpr enables this to be used in Expects Expects(0<=n && n F.7: For general use, take `T*` arguments rather than a smart pointers +### F.7: For general use, take `T*` arguments rather than smart pointers **Reason**: Passing a smart pointer transfers or shares ownership. Passing by smart pointer restricts the use of a function to callers that use smart pointers. @@ -1861,7 +1865,7 @@ Passing a shared smart pointer (e.g., `std::shared_ptr`) implies a run-time cost **Note**: `constexpr` functions are pure. -**Enforcement**: not possible. +**Enforcement**: Not possible. ## F.call: Argument passing @@ -1893,7 +1897,7 @@ If you have multiple values to return, [use a tuple](#Rf-T-multi) or similar mul * For non-value types, such as types in an inheritance hierarchy, return the object by `unique_ptr` or `shared_ptr`. * If a type is expensive to move (e.g., `array`), consider allocating it on the free store and return a handle (e.g., `unique_ptr`), or passing it in a non-`const` reference to a target object to fill (to be used as an out-parameter). -* In the special case of allowing a caller 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 instead and pass by `&`. This one use of the more generally named "caller-allocated out" pattern. +* In the special case of allowing a caller 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 instead and pass by `&`. This is one use of the more generally named "caller-allocated out" pattern. **For an "in-out" parameter:** Pass by non-`const` reference. This makes it clear to callers that the object is assumed to be modified. @@ -1947,7 +1951,7 @@ such as passing a `const int&`, returning an `array` by value, and retur ### F.16: Use `T*` or `owner` to designate a single object -**Reason**: In traditional C and C++ code, "Plain `T*` is used for many weakly-related purposes, such as +**Reason**: In traditional C and C++ code, plain `T*` is used for many weakly-related purposes, such as * Identify a (single) object (not to be deleted by this function) * Point to an object allocated on the free store (and delete it later) @@ -1956,7 +1960,6 @@ such as passing a `const int&`, returning an `array` by value, and retur * Identify an array with a length specified separately * Identify a location in an array -Using separate names for each of these uses improves code quality because confusion about the meaning of any particular `T*` is the source of many serious errors. For example, `not_null` makes it obvious to a reader (human or machine) that a test for `nullptr` is not necessary before dereference. Additionally, when debugging, `owner` and `not_null` can be instrumented to check for correctness. @@ -1971,11 +1974,11 @@ When I call `length(r)` should I test for `r==nullptr` first? Should the impleme int length(Record* p); // the implementor of length() must assume that p==nullptr is possible -**Note**: A `not_null` is assumed not to be the `nullptr`; a `T*` may be the `nullptr`; both can be represented in memory as a `T*` (so no run-time overhead is implied). +**Note**: A `not_null` is assumed not to be the `nullptr`; a `T*` may be the `nullptr`; both can be represented in memory as a `T*` (so no run-time overhead is implied). **Note**: `owner` represents ownership. -**Also**: Assume that a `T*` obtained from a smart pointer to `T` (e.g., unique_ptr<`T`>) points to a single element. +**Also**: Assume that a `T*` obtained from a smart pointer to `T` (e.g., `unique_ptr`) points to a single element. **See also**: [Support library](#S-gsl). @@ -2014,7 +2017,7 @@ When I call `length(r)` should I test for `r==nullptr` first? Should the impleme **Example**: - X* find(array_view r, const X& v) // find v in r + X* find(array_view r, const X& v); // find v in r vector vec; // ... @@ -2022,7 +2025,7 @@ When I call `length(r)` should I test for `r==nullptr` first? Should the impleme **Note**: Ranges are extremely common in C++ code. Typically, they are implicit and their correct use is very hard to ensure. In particular, given a pair of arguments `(p, n)` designating an array [`p`:`p+n`), it is in general impossible to know if there really are n elements to access following `*p`. `array_view` and `array_view_p` are simple helper classes designating a [p:q) range and a range starting with p and ending with the first element for which a predicate is true, respectively. -**Note**: an `array_view` object does not own its elements and is so small that it can be passed by value. +**Note**: An `array_view` object does not own its elements and is so small that it can be passed by value. **Note**: Passing an `array_view` object as an argument is exactly as efficient as passing a pair of pointer arguments or passing a pointer and an integer count. @@ -2140,7 +2143,7 @@ If the writer of `g()` makes an assumption about the size of `buffer` a bad logi void fill(Package&); // OK int val(); // OK - val(int&); // Bad: Is val reading its argument + void val(int&); // Bad: Is val reading its argument **Enforcement**: Hard to choose a cutover value for the size of the value returned. @@ -2181,8 +2184,8 @@ If you have performance justification to optimize for rvalues, overload on `&&` **Enforcement**: -* Flag all `X&&` parameters (where `X` is not a template type parameter name) and uses it without `std::move`. -* Flag access to moved-from objects +* Flag all `X&&` parameters (where `X` is not a template type parameter name) and code that uses them without `std::move`. +* Flag access to moved-from objects. ### F.26: Use a `unique_ptr` to transfer ownership where a pointer is needed @@ -2213,7 +2216,7 @@ If you have performance justification to optimize for rvalues, overload on `&&` **Example**: - shared_ptr im { read_image(somewhere); }; + shared_ptr im { read_image(somewhere) }; std::thread t0 {shade, args0, top_left, im}; std::thread t1 {shade, args1, top_right, im}; @@ -2369,8 +2372,8 @@ This applies to references as well: Here on one popular implementation I got the output - *p == 9 - cx == 999 + *p == 999 + gx == 999 I expected that because the call of `g()` reuses the stack space abandoned by the call of `f()` so `*p` refers to the space now occupied by `gx`. @@ -2381,7 +2384,7 @@ Imagine what a cracker could do with that dangling pointer. Fortunately, most (all?) modern compilers catch and warn against this simple case. -**Note**: you can construct similar examples using references. +**Note**: You can construct similar examples using references. **Note**: This applies only to non-`static` local variables. All `static` variables are (as their name indicates) statically allocated, so that pointers to them cannot dangle. @@ -2557,7 +2560,7 @@ For passthrough functions that pass in parameters (by ordinary reference or by p # C: Classes and Class Hierarchies -A class is a user-defined type, for which a programmer can define the representation, operations, and interfaces. +A class is a user-defined type, for which a programmer can define the representation, operations, and interfaces. Class hierarchies are used to organize related classes into hierarchical structures. Class rule summary: @@ -2593,14 +2596,14 @@ Subsections: **Note**: From a language perspective `class` and `struct` differ only in the default visibility of their members. -**Enforcement**: Probably impossible. Maybe a heuristic looking for date items used together is possible. +**Enforcement**: Probably impossible. Maybe a heuristic looking for data items used together is possible. ### C.2: Use `class` if the class has an invariant; use `struct` if the data members can vary independently -**Reason**: Ease of comprehension. The use of `class` alerts the programmer to the need for an invariant +**Reason**: Ease of comprehension. The use of `class` alerts the programmer to the need for an invariant. -**Note**: An invariant is logical condition for the members of an object that a constructor must establish for the public member functions to assume. After the invariant is established (typically by a constructor) every member function can be called for the object. An invariant can be stated informally (e.g., in a comment) or more formally using `Expects`. +**Note**: An invariant is a logical condition for the members of an object that a constructor must establish for the public member functions to assume. After the invariant is established (typically by a constructor) every member function can be called for the object. An invariant can be stated informally (e.g., in a comment) or more formally using `Expects`. **Example**: @@ -2626,7 +2629,7 @@ but ### C.3: Represent the distinction between an interface and an implementation using a class -**Reason**: an explicit distinction between interface and implementation improves readability and simplifies maintenance. +**Reason**: An explicit distinction between interface and implementation improves readability and simplifies maintenance. **Example**: