From 2cc9512035b722e423ae8be9ce0dba747d2f933c Mon Sep 17 00:00:00 2001 From: Sergey Zubkov Date: Sun, 10 Apr 2022 23:37:33 -0400 Subject: [PATCH] update from master --- CppCoreGuidelines.md | 458 ++++++++++++++++++++----------------------- 1 file changed, 214 insertions(+), 244 deletions(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 99a5907..5cf7255 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1,6 +1,6 @@ # C++ Core Guidelines -January 3, 2022 +April 10, 2022 Editors: @@ -187,7 +187,7 @@ You can look at design concepts used to express the rules: This document is a set of guidelines for using C++ well. The aim of this document is to help people to use modern C++ effectively. -By "modern C++" we mean effective use of the ISO C++ standard (currently C++17, but almost all of our recommendations also apply to C++14 and C++11). +By "modern C++" we mean effective use of the ISO C++ standard (currently C++20, but almost all of our recommendations also apply to C++17, C++14 and C++11). In other words, what would you like your code to look like in 5 years' time, given that you can start now? In 10 years' time? The guidelines are focused on relatively high-level issues, such as interfaces, resource management, memory management, and concurrency. @@ -224,7 +224,7 @@ We plan to modify and extend this document as our understanding improves and the # In: Introduction -This is a set of core guidelines for modern C++ (currently C++17) taking likely future enhancements and ISO Technical Specifications (TSs) into account. +This is a set of core guidelines for modern C++ (currently C++20 and C++17) taking likely future enhancements and ISO Technical Specifications (TSs) into account. The aim is to help C++ programmers to write simpler, more efficient, more maintainable code. Introduction summary: @@ -594,7 +594,7 @@ In such cases, control their (dis)use with an extension of these Coding Guidelin ##### Enforcement -Use an up-to-date C++ compiler (currently C++17, C++14, or C++11) with a set of options that do not accept extensions. +Use an up-to-date C++ compiler (currently C++20 or C++17) with a set of options that do not accept extensions. ### P.3: Express intent @@ -1432,7 +1432,7 @@ Consider: Callers are unsure what types are allowed and if the data may be mutated as `const` is not specified. Note all pointer types -implicitly convert to void*, so it is easy for callers to provide this value. +implicitly convert to `void*`, so it is easy for callers to provide this value. The callee must `static_cast` data to an unverified type to use it. That is error-prone and verbose. @@ -1479,7 +1479,7 @@ This design is more explicit, safe and legible: s.frequency = alarm_settings::every_10_seconds; set_settings(s); -For the case of a set of boolean values consider using a flags enum; a pattern that expresses a set of boolean values. +For the case of a set of boolean values consider using a flags `enum`; a pattern that expresses a set of boolean values. enable_lamp_options(lamp_option::on | lamp_option::animate_state_transitions); @@ -1758,22 +1758,17 @@ Make the interface precisely specified and compile-time checkable in the (not so Use the C++20 style of requirements specification. For example: template - // requires InputIterator && EqualityComparable, Val> + requires input_iterator && equality_comparable_with, Val> Iter find(Iter first, Iter last, Val v) { // ... } -##### Note - -Soon (in C++20), all compilers will be able to check `requires` clauses once the `//` is removed. -Concepts are supported in GCC 6.1 and later. - **See also**: [Generic programming](#SS-GP) and [concepts](#SS-concepts). ##### Enforcement -(Not yet enforceable) 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). +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 @@ -2077,10 +2072,11 @@ To really reduce the number of arguments, we need to bundle the arguments into h Grouping arguments into "bundles" is a general technique to reduce the number of arguments and to increase the opportunities for checking. -Alternatively, we could use concepts (as defined by the ISO TS) to define the notion of three types that must be usable for merging: +Alternatively, we could use a standard library concept to define the notion of three types that must be usable for merging: - Mergeable{In1, In2, Out} - OutputIterator merge(In1 r1, In2 r2, Out result); + template + requires mergeable + Out merge(In1 r1, In2 r2, Out result); ##### Example @@ -2820,10 +2816,10 @@ We can catch many common cases of dangling pointers statically (see [lifetime sa * Flag a parameter of a smart pointer type (a type that overloads `operator->` or `operator*`) that is copyable/movable but never copied/moved from in the function body, and that is never modified, and that is not passed along to another function that could do so. That means the ownership semantics are not used. Suggest using a `T*` or `T&` instead. -**see also**: +**See also**: -* [prefer `t*` over `t&` when "no argument" is a valid option](#rf-ptr-ref) -* [smart pointer rule summary](#rr-summary-smartptrs) +* [Prefer `T*` over `T&` when "no argument" is a valid option](#Rf-ptr-ref) +* [Smart pointer rule summary](#Rr-summary-smartptrs) ### F.8: Prefer pure functions @@ -2849,12 +2845,24 @@ Suppression of unused parameter warnings. ##### Example - X* find(map& m, const string& s, Hint); // once upon a time, a hint was used + widget* find(const set& s, const widget& w, Hint); // once upon a time, a hint was used ##### Note Allowing parameters to be unnamed was introduced in the early 1980 to address this problem. +If parameters are conditionally unused, declare them with the `[[maybe_unused]]` attribute. +For example: + + template + Value* find(const set& s, const Value& v, [[maybe_unused]] Hint h) + { + if constexpr (sizeof(Value) > CacheSize) + { + // a hint is used only if Value is of a certain size + } + } + ##### Enforcement Flag named unused parameters. @@ -3741,6 +3749,7 @@ Declaring `main` (the one global `main` of a program) `void` limits portability. ##### Note We mention this only because of the persistence of this error in the community. +Note that despite its non-void return type, the main function does not require an explicit return statement. ##### Enforcement @@ -4473,7 +4482,7 @@ Prefer the order `public` members before `protected` members before `private` me ##### Enforcement * [Flag protected data](#Rh-protected). -* Flag mixtures of `public` and private `data` +* Flag mixtures of `public` and `private` data ## C.concrete: Concrete types @@ -8084,7 +8093,7 @@ Casting to a reference expresses that you intend to end up with a valid object, The `dynamic_cast` conversion allows to test whether a pointer is pointing at a polymorphic object that has a given class in its hierarchy. Since failure to find the class merely returns a null value, it can be tested during run time. This allows writing code that can choose alternative paths depending on the results. -Contrast with [C.147](#Rh-ptr-cast), where failure is an error, and should not be used for conditional execution. +Contrast with [C.147](#Rh-ref-cast), where failure is an error, and should not be used for conditional execution. ##### Example @@ -10429,7 +10438,7 @@ A structured binding (C++17) is specifically designed to introduce several varia or better using concepts: - bool any_of(InputIterator first, InputIterator last, Predicate pred); + bool any_of(input_iterator auto first, input_iterator auto last, predicate auto pred); ##### Example @@ -10495,10 +10504,10 @@ Avoid `auto` for initializer lists and in cases where you know exactly which typ ##### Note -When concepts become available, we can (and should) be more specific about the type we are deducing: +As of C++20, we can (and should) use concepts to be more specific about the type we are deducing: // ... - ForwardIterator p = algo(x, y, z); + forward_iterator auto p = algo(x, y, z); ##### Example (C++17) @@ -12814,11 +12823,11 @@ consider `gsl::finally()` as a cleaner and more reliable alternative to `goto ex switch(x) { case 1 : while (/* some condition */) { - //... + // ... break; - } //Oops! break switch or break while intended? + } // Oops! break switch or break while intended? case 2 : - //... + // ... break; } @@ -12858,14 +12867,14 @@ Often, a loop that requires a `break` is a good candidate for a function (algori Often, a loop that uses `continue` can equivalently and as clearly be expressed by an `if`-statement. - for (int item : vec) { //BAD + 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 + for (int item : vec) { // GOOD if (item%2 != 0 && item != 5 && item <= 10) { /* do something with item */ } @@ -12912,6 +12921,7 @@ Multiple case labels of a single statement is OK: } Return statements in a case label are also OK: + switch (x) { case 'a': return 1; @@ -12953,7 +12963,7 @@ Flag all implicit fallthroughs from non-empty `case`s. ##### Example - enum E { a, b, c , d }; + enum E { a, b, c, d }; void f1(E x) { @@ -13107,10 +13117,10 @@ Helps make style consistent and conventional. By definition, a condition in an `if`-statement, `while`-statement, or a `for`-statement selects between `true` and `false`. A numeric value is compared to `0` and a pointer value to `nullptr`. - // These all mean "if `p` is not `nullptr`" + // These all mean "if p is not nullptr" if (p) { ... } // good - if (p != 0) { ... } // redundant `!=0`; bad: don't use 0 for pointers - if (p != nullptr) { ... } // redundant `!=nullptr`, not recommended + if (p != 0) { ... } // redundant !=0, bad: don't use 0 for pointers + if (p != nullptr) { ... } // redundant !=nullptr, not recommended Often, `if (p)` is read as "if `p` is valid" which is a direct expression of the programmers intent, whereas `if (p != nullptr)` would be a long-winded workaround. @@ -13167,10 +13177,10 @@ would not in itself save you. The opposite condition is most easily expressed using a negation: - // These all mean "if `p` is `nullptr`" + // These all mean "if p is nullptr" if (!p) { ... } // good - if (p == 0) { ... } // redundant `== 0`; bad: don't use `0` for pointers - if (p == nullptr) { ... } // redundant `== nullptr`, not recommended + if (p == 0) { ... } // redundant == 0, bad: don't use 0 for pointers + if (p == nullptr) { ... } // redundant == nullptr, not recommended ##### Enforcement @@ -13700,11 +13710,11 @@ We can do better (in C++98) Here, we use the compiler's knowledge about the size of the array, the type of elements, and how to compare `double`s. -With C++11 plus [concepts](#SS-concepts), we can do better still +With C++20, we can do better still - // Sortable specifies that c must be a + // sortable specifies that c must be a // random-access sequence of elements comparable with < - void sort(Sortable& c); + void sort(sortable auto& c); sort(c); @@ -13714,10 +13724,10 @@ They implicitly rely on the element type having less-than (`<`) defined. To complete the interface, we need a second version that accepts a comparison criteria: // compare elements of c using p - void sort(Sortable& c, Predicate> p); + template requires sortable + void sort(R&& r, C c); -The standard-library specification of `sort` offers those two versions, -but the semantics is expressed in English rather than code using concepts. +The standard-library specification of `sort` offers those two versions, and more. ##### Note @@ -15992,80 +16002,56 @@ Sometimes, [`finally()`](#Re-finally) can make such unsystematic cleanup a bit m ##### Reason -A user-defined type is unlikely to clash with other people's exceptions. +A user-defined type can better transmit information about an error to a handler. Information +can be encoded into the type itself and the type is unlikely to clash with other people's exceptions. ##### Example - void my_code() + throw 7; // bad + + throw "something bad"; // bad + + throw std::exception{}; // bad - no info + +Deriving from `std::exception` gives the flexibility to catch the specific exception or handle generally through `std::exception`: + + class MyException : public std::runtime_error { + public: + MyException(const string& msg) : std::runtime_error{msg} {} // ... - throw Moonphase_error{}; - // ... - } + }; - void your_code() - { - try { - // ... - my_code(); - // ... - } - catch(const Bufferpool_exhausted&) { - // ... - } - } + // ... -##### Example, don't + throw MyException{"something bad"}; // good - void my_code() // Don't - { - // ... - throw 7; // 7 means "moon in the 4th quarter" - // ... - } +Exceptions do not need to be derived from `std::exception`: - void your_code() // Don't - { - try { - // ... - my_code(); - // ... - } - catch(int i) { // i == 7 means "input buffer too small" - // ... - } - } + class MyCustomError final {}; // not derived from std::exception -##### Note + // ... -The standard-library classes derived from `exception` should be used only as base classes or for exceptions that require only "generic" handling. Like built-in types, their use could clash with other people's use of them. + throw MyCustomError{}; // good - handlers must catch this type (or ...) -##### Example, don't +Library types derived from `std::exception` can be used as generic exceptions if +no useful information can be added at the point of detection: - void my_code() // Don't - { - // ... - throw runtime_error{"moon in the 4th quarter"}; - // ... - } + throw std::runtime_error("someting bad"); // good - void your_code() // Don't - { - try { - // ... - my_code(); - // ... - } - catch(const runtime_error&) { // runtime_error means "input buffer too small" - // ... - } - } + // ... + + throw std::invalid_argument("i is not even"); // good -**See also**: [Discussion](#Sd-???) +`enum` classes are also allowed: + + enum class alert {RED, YELLOW, GREEN}; + + throw alert::RED; // good ##### Enforcement -Catch `throw` and `catch` of a built-in type. Maybe warn about `throw` and `catch` using a standard-library `exception` type. Obviously, exceptions derived from the `std::exception` hierarchy are fine. +Catch `throw` of built-in types and `std::exception`. ### E.15: Throw by value, catch exceptions from a hierarchy by reference @@ -16828,11 +16814,7 @@ In C++, these requirements are expressed by compile-time predicates called conce Templates can also be used for meta-programming; that is, programs that compose code at compile time. 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). -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. -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. +"Concepts" were standardized in C++20, although they were first made available, in slightly older syntax, in GCC 6.1. Template use rule summary: @@ -16938,7 +16920,7 @@ Generality. Reuse. Efficiency. Encourages consistent definition of user types. Conceptually, the following requirements are wrong because what we want of `T` is more than just the very low-level concepts of "can be incremented" or "can be added": template - // requires Incrementable + requires Incrementable T sum1(vector& v, T s) { for (auto x : v) s += x; @@ -16946,7 +16928,7 @@ Conceptually, the following requirements are wrong because what we want of `T` i } template - // requires Simple_number + requires Simple_number T sum2(vector& v, T s) { for (auto x : v) s = s + x; @@ -16959,7 +16941,7 @@ And, in this case, missed an opportunity for a generalization. ##### Example template - // requires Arithmetic + requires Arithmetic T sum(vector& v, T s) { for (auto x : v) s += x; @@ -16983,14 +16965,6 @@ We aim to minimize requirements on template arguments, but the absolutely minima Templates can be used to express essentially everything (they are Turing complete), but the aim of generic programming (as expressed using templates) is to efficiently generalize operations/algorithms over a set of types with similar semantic properties. -##### Note - -The `requires` in the comments are uses of `concepts`. -"Concepts" are defined in an ISO Technical Specification: [concepts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf). -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. -If you use GCC 6.1 or later, you can uncomment them. - ##### Enforcement * Flag algorithms with "overly simple" requirements, such as direct use of specific operators without a concept. @@ -17161,9 +17135,8 @@ See the reference to more specific rules. ## T.concepts: Concept rules -Concepts is a facility for specifying requirements for template arguments. -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 is a C++20 facility for specifying requirements for template arguments. +They are crucial in the thinking about generic programming and the basis of much work on future C++ libraries (standard and other). This section assumes concept support @@ -17201,8 +17174,8 @@ Specifying concepts for template arguments is a powerful design tool. ##### Example template - // requires Input_iterator - // && Equality_comparable, Val> + requires input_iterator + && equality_comparable_with, Val> Iter find(Iter b, Iter e, Val v) { // ... @@ -17210,24 +17183,8 @@ Specifying concepts for template arguments is a powerful design tool. or equivalently and more succinctly: - template - // requires Equality_comparable, Val> - Iter find(Iter b, Iter e, Val v) - { - // ... - } - -##### Note - -"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). -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. -If you use GCC 6.1 or later, you can uncomment them: - - template - requires Input_iterator - && Equality_comparable, Val> + template + requires equality_comparable_with, Val> Iter find(Iter b, Iter e, Val v) { // ... @@ -17239,7 +17196,7 @@ Plain `typename` (or `auto`) is the least constraining concept. It should be used only rarely when nothing more than "it's a type" can be assumed. This is typically only needed when (as part of template metaprogramming code) we manipulate pure expression trees, postponing type checking. -**References**: TC++PL4, Palo Alto TR, Sutton +**References**: TC++PL4 ##### Enforcement @@ -17249,26 +17206,26 @@ Flag template type arguments without concepts ##### 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 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. ##### Note Unless you are creating a new generic library, most of the concepts you need will already be defined by the standard library. -##### Example (using TS concepts) +##### Example template - // don't define this: Sortable is in the GSL + // don't define this: sortable is in concept Ordered_container = Sequence && Random_access> && Ordered>; - void sort(Ordered_container& s); + void sort(Ordered_container auto& s); -This `Ordered_container` is quite plausible, but it is very similar to the `Sortable` concept in the GSL (and the Range TS). +This `Ordered_container` is quite plausible, but it is very similar to the `sortable` concept in the standard library. Is it better? Is it right? Does it accurately reflect the standard's requirements for `sort`? -It is better and simpler just to use `Sortable`: +It is better and simpler just to use `sortable`: - void sort(Sortable& s); // better + void sort(sortable auto& s); // better ##### Note @@ -17291,11 +17248,11 @@ Hard. `auto` is the weakest concept. Concept names convey more meaning than just `auto`. -##### Example (using TS concepts) +##### Example vector v{ "abc", "xyz" }; - auto& x = v.front(); // bad - String& s = v.front(); // good (String is a GSL concept) + auto& x = v.front(); // bad + String auto& s = v.front(); // good (String is a GSL concept) ##### Enforcement @@ -17307,29 +17264,21 @@ Hard. Readability. Direct expression of an idea. -##### Example (using TS concepts) +##### Example -To say "`T` is `Sortable`": +To say "`T` is `sortable`": template // Correct but verbose: "The parameter is - // requires Sortable // of type T which is the name of a type - void sort(T&); // that is Sortable" + requires sortable // of type T which is the name of a type + void sort(T&); // that is sortable" - template // Better (assuming support for concepts): "The parameter is of type T + template // Better: "The parameter is of type T void sort(T&); // which is Sortable" - void sort(Sortable&); // Best (assuming support for concepts): "The parameter is Sortable" + void sort(sortable auto&); // Best: "The parameter is Sortable" The shorter versions better match the way we speak. Note that many templates don't need to use the `template` keyword. -##### Note - -"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). -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. -If you use a compiler that supports concepts (e.g., GCC 6.1 or later), you can remove the `//`. - ##### Enforcement * Not feasible in the short term when people convert from the `` and ` notation. @@ -17342,7 +17291,7 @@ Concepts are meant to represent fundamental concepts in an application domain (h Similarly throwing together a set of syntactic constraints to be used for the arguments for a single class or algorithm is not what concepts were designed for and will not give the full benefits of the mechanism. -Obviously, defining concepts will be most useful for code that can use an implementation (e.g., GCC 6.1 or later), +Obviously, defining concepts is most useful for code that can use an implementation (e.g., C++20 or later) but defining concepts is in itself a useful design technique and help catch conceptual errors and clean up the concepts (sic!) of an implementation. ### T.20: Avoid "concepts" without meaningful semantics @@ -17353,12 +17302,14 @@ Concepts are meant to express semantic notions, such as "a number", "a range" of Simple constraints, such as "has a `+` operator" and "has a `>` operator" cannot be meaningfully specified in isolation and should be used only as building blocks for meaningful concepts, rather than in user code. -##### Example, bad (using TS concepts) +##### Example, bad template - concept Addable = has_plus; // bad; insufficient + // bad; insufficient + concept Addable = requires(T a, T b) { a+b; }; - template auto algo(const N& a, const N& b) // use two numbers + template + auto algo(const N& a, const N& b) // use two numbers { // ... return a + b; @@ -17379,16 +17330,14 @@ This `Addable` violates the mathematical rule that addition is supposed to be co The ability to specify meaningful semantics is a defining characteristic of a true concept, as opposed to a syntactic constraint. -##### Example (using TS concepts) +##### Example template // The operators +, -, *, and / for a number are assumed to follow the usual mathematical rules - concept Number = has_plus - && has_minus - && has_multiply - && has_divide; + concept Number = requires(T a, T b) { a+b; a-b; a*b; a/b; }; - template auto algo(const N& a, const N& b) + template + auto algo(const N& a, const N& b) { // ... return a + b; @@ -17424,9 +17373,9 @@ Helps implementers and maintainers. This is a specific variant of the general rule that [a concept must make semantic sense](#Rt-low). -##### Example, bad (using TS concepts) +##### Example, bad - template concept Subtractable = requires(T a, T, b) { a-b; }; + template concept Subtractable = requires(T a, T b) { a-b; }; This makes no semantic sense. You need at least `+` to make `-` meaningful and useful. @@ -17479,7 +17428,7 @@ The rule supports the view that a concept should reflect a (mathematically) cohe // ... and the other comparison operators ... Minimal operator+(const Convenient&, const Convenient&); - // .. and the other arithmetic operators ... + // ... and the other arithmetic operators ... void f(const Convenient& x, const Convenient& y) { @@ -17510,17 +17459,17 @@ A meaningful/useful concept has a semantic meaning. Expressing these semantics in an informal, semi-formal, or formal way makes the concept comprehensible to readers and the effort to express it can catch conceptual errors. Specifying semantics is a powerful design tool. -##### Example (using TS concepts) +##### Example template // The operators +, -, *, and / for a number are assumed to follow the usual mathematical rules // axiom(T a, T b) { a + b == b + a; a - a == 0; a * (b + c) == a * b + a * c; /*...*/ } concept Number = requires(T a, T b) { - {a + b} -> T; // the result of a + b is convertible to T - {a - b} -> T; - {a * b} -> T; - {a / b} -> T; - } + {a + b} -> convertible_to; + {a - b} -> convertible_to; + {a * b} -> convertible_to; + {a / b} -> convertible_to; + }; ##### Note @@ -17539,18 +17488,18 @@ Once language support is available, the `//` in front of the axiom can be remove The GSL concepts have well-defined semantics; see the Palo Alto TR and the Ranges TS. -##### Exception (using TS concepts) +##### Exception Early versions of a new "concept" still under development will often just define simple sets of constraints without a well-specified semantics. Finding good semantics can take effort and time. An incomplete set of constraints can still be very useful: // balancer for a generic binary tree - template concept bool Balancer = requires(Node* p) { + template concept Balancer = requires(Node* p) { add_fixup(p); touch(p); detach(p); - } + }; So a `Balancer` must supply at least these operations on a tree `Node`, but we are not yet ready to specify detailed semantics because a new kind of balanced tree might require more operations @@ -17571,13 +17520,15 @@ Each new use case might require such an incomplete concept to be improved. Otherwise they cannot be distinguished automatically by the compiler. -##### Example (using TS concepts) +##### Example template - concept bool Input_iter = requires(I iter) { ++iter; }; + // Note: input_iterator is defined in + concept Input_iter = requires(I iter) { ++iter; }; template - concept bool Fwd_iter = Input_iter && requires(I iter) { iter++; } + // Note: forward_iterator is defined in + concept Fwd_iter = Input_iter && requires(I iter) { iter++; }; The compiler can determine refinement based on the sets of required operations (here, suffix `++`). This decreases the burden on implementers of these types since @@ -17595,23 +17546,25 @@ To disambiguate them, see [T.24](#Rt-tag). Two concepts requiring the same syntax but having different semantics leads to ambiguity unless the programmer differentiates them. -##### Example (using TS concepts) +##### Example template // iterator providing random access - concept bool RA_iter = ...; + // Note: random_access_iterator is defined in + concept RA_iter = ...; template // iterator providing random access to contiguous data - concept bool Contiguous_iter = - RA_iter && is_contiguous::value; // using is_contiguous trait + // Note: contiguous_iterator is defined in + concept Contiguous_iter = + RA_iter && is_contiguous_v; // using is_contiguous trait The programmer (in a library) must define `is_contiguous` (a trait) appropriately. Wrapping a tag class into a concept leads to a simpler expression of this idea: - template concept Contiguous = is_contiguous::value; + template concept Contiguous = is_contiguous_v; template - concept bool Contiguous_iter = RA_iter && Contiguous; + concept Contiguous_iter = RA_iter && Contiguous; The programmer (in a library) must define `is_contiguous` (a trait) appropriately. @@ -17633,7 +17586,7 @@ Prefer the standard-library ones. Clarity. Maintainability. Functions with complementary requirements expressed using negation are brittle. -##### Example (using TS concepts) +##### Example Initially, people will try to define functions with complementary requirements: @@ -17699,21 +17652,21 @@ Now the opportunities for errors multiply. The definition is more readable and corresponds directly to what a user has to write. Conversions are taken into account. You don't have to remember the names of all the type traits. -##### Example (using TS concepts) +##### Example You might be tempted to define a concept `Equality` like this: template concept Equality = has_equal && has_not_equal; -Obviously, it would be better and easier just to use the standard `EqualityComparable`, +Obviously, it would be better and easier just to use the standard `equality_comparable`, but - just as an example - if you had to define such a concept, prefer: template concept Equality = requires(T a, T b) { - bool == { a == b } - bool == { a != b } + { a == b } -> std::convertible_to; + { a != b } -> std::convertible_to; // axiom { !(a == b) == (a != b) } // axiom { a = b; => a == b } // => means "implies" - } + }; as opposed to defining two meaningless concepts `has_equal` and `has_not_equal` just as helpers in the definition of `Equality`. By "meaningless" we mean that we cannot specify the semantics of `has_equal` in isolation. @@ -17736,22 +17689,22 @@ However, the interface to a template is a critical concept - a contract between Function objects can carry more information through an interface than a "plain" pointer to function. In general, passing function objects gives better performance than passing pointers to functions. -##### Example (using TS concepts) +##### Example bool greater(double x, double y) { return x > y; } sort(v, greater); // pointer to function: potentially slow sort(v, [](double x, double y) { return x > y; }); // function object - sort(v, std::greater<>); // function object + sort(v, std::greater{}); // function object bool greater_than_7(double x) { return x > 7; } auto x = find_if(v, greater_than_7); // pointer to function: inflexible auto y = find_if(v, [](double x) { return x > 7; }); // function object: carries the needed data auto z = find_if(v, Greater_than(7)); // function object: carries the needed data -You can, of course, generalize those functions using `auto` or (when and where available) concepts. For example: +You can, of course, generalize those functions using `auto` or concepts. For example: - auto y1 = find_if(v, [](Ordered x) { return x > 7; }); // require an ordered type - auto z1 = find_if(v, [](auto x) { return x > 7; }); // hope that the type has a > + auto y1 = find_if(v, [](totally_ordered auto x) { return x > 7; }); // require an ordered type + auto z1 = find_if(v, [](auto x) { return x > 7; }); // hope that the type has a > ##### Note @@ -17773,11 +17726,11 @@ The performance argument depends on compiler and optimizer technology. Keep interfaces simple and stable. -##### Example (using TS concepts) +##### Example Consider, a `sort` instrumented with (oversimplified) simple debug support: - void sort(Sortable& s) // sort sequence s + void sort(sortable auto& s) // sort sequence s { if (debug) cerr << "enter sort( " << s << ")\n"; // ... @@ -17786,7 +17739,7 @@ Consider, a `sort` instrumented with (oversimplified) simple debug support: Should this be rewritten to: - template + template requires Streamable void sort(S& s) // sort sequence s { @@ -17795,7 +17748,7 @@ Should this be rewritten to: if (debug) cerr << "exit sort( " << s << ")\n"; } -After all, there is nothing in `Sortable` that requires `iostream` support. +After all, there is nothing in `sortable` that requires `iostream` support. On the other hand, there is nothing in the fundamental idea of sorting that says anything about debugging. ##### Note @@ -17950,14 +17903,14 @@ Most uses support that anyway. explicit X(int); X(const X&); // copy X operator=(const X&); - X(X&&) noexcept; // move + X(X&&) noexcept; // move X& operator=(X&&) noexcept; ~X(); // ... no more constructors ... }; - X x {1}; // fine - X y = x; // fine + X x {1}; // fine + X y = x; // fine std::vector v(10); // error: no default constructor ##### Note @@ -18381,7 +18334,7 @@ There are three major ways to let calling code customize a template. void test2(T t) // 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 } template @@ -18698,7 +18651,7 @@ If you feel the need to hide your template metaprogramming in macros, you have p ##### Reason -Until concepts become generally available, we need to emulate them using TMP. +Where C++20 is not available, we need to emulate them using TMP. Use cases that require concepts (e.g. overloading based on concepts) are among the most common (and simple) uses of TMP. ##### Example @@ -18715,9 +18668,9 @@ Use cases that require concepts (e.g. overloading based on concepts) are among t Such code is much simpler using concepts: - void advance(RandomAccessIterator p, int n) { p += n; } + void advance(random_access_iterator auto p, int n) { p += n; } - void advance(ForwardIterator p, int n) { assert(n >= 0); while (n--) ++p;} + void advance(forward_iterator auto p, int n) { assert(n >= 0); while (n--) ++p;} ##### Enforcement @@ -18980,7 +18933,7 @@ You can't partially specialize a function template per language rules. You can f ##### Reason -If you intend for a class to match a concept, verifying that early saves users pain. +If you intend for a class to match a concept, verifying that early saves users' pain. ##### Example @@ -19519,7 +19472,7 @@ For example: #include #include -a user can now get that set of declarations with a single `#include`" +a user can now get that set of declarations with a single `#include` #include "basic_std_lib.h" @@ -19606,7 +19559,26 @@ It is almost always a bug to mention an unnamed namespace in a header file. ##### Example - ??? + // file foo.h: + namespace + { + const double x = 1.234; // bad + + double foo(double y) // bad + { + return y + x; + } + } + + namespace Foo + { + const double x = 1.234; // good + + inline double foo(double y) // good + { + return y + x; + } + } ##### Enforcement @@ -19745,7 +19717,7 @@ For a variable-length array, use `std::vector`, which additionally can change it int v[SIZE]; // BAD - std::array w; // ok + std::array w; // ok ##### Example @@ -19853,7 +19825,7 @@ Also, `std::array<>::fill()` or `std::fill()` or even an empty initializer are b array a, b, c{}; // c is initialized to zero a.fill(0); fill(b.begin(), b.end(), 0); // std::fill() - fill(b, 0); // std::fill() + Ranges TS + fill(b, 0); // std::ranges::fill() if ( a == b ) { // ... @@ -21239,27 +21211,25 @@ These concepts (type predicates) are borrowed from Andrew Sutton's Origin library, the Range proposal, and the ISO WG21 Palo Alto TR. -They are likely to be very similar to what will become part of the ISO C++ standard. -The notation is that of the ISO WG21 [Concepts TS](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf). -Most of the concepts below are defined in [the Ranges TS](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf). +Many of them are very similar to what became part of the ISO C++ standard in C++20. -* `Range` -* `String` // ??? -* `Number` // ??? -* `Sortable` -* `EqualityComparable` -* `Convertible` -* `Common` +* `String` +* `Number` * `Boolean` -* `Integral` -* `SignedIntegral` -* `SemiRegular` // in C++20, `std::semiregular` -* `Regular` // in C++20, `std::regular` -* `TotallyOrdered` -* `Function` -* `RegularFunction` -* `Predicate` -* `Relation` +* `Range` // in C++20, `std::ranges::range` +* `Sortable` // in C++20, `std::sortable` +* `EqualityComparable` // in C++20, `std::equality_comparable` +* `Convertible` // in C++20, `std::convertible_to` +* `Common` // in C++20, `std::common_with` +* `Integral` // in C++20, `std::integral` +* `SignedIntegral` // in C++20, `std::signed_integral` +* `SemiRegular` // in C++20, `std::semiregular` +* `Regular` // in C++20, `std::regular` +* `TotallyOrdered` // in C++20, `std::totally_ordered` +* `Function` // in C++20, `std::invocable` +* `RegularFunction` // in C++20, `std::regular_invocable` +* `Predicate` // in C++20, `std::predicate` +* `Relation` // in C++20, `std::relation` * ... ### GSL.ptr: Smart pointer concepts @@ -21916,7 +21886,7 @@ This section covers answers to frequently asked questions about these guidelines ### FAQ.1: What do these guidelines aim to achieve? -See the top of this page. This is an open-source project to maintain modern authoritative guidelines for writing C++ code using the current C++ Standard (as of this writing, C++14). The guidelines are designed to be modern, machine-enforceable wherever possible, and open to contributions and forking so that organizations can easily incorporate them into their own corporate coding guidelines. +See the top of this page. This is an open-source project to maintain modern authoritative guidelines for writing C++ code using the current C++ Standard. The guidelines are designed to be modern, machine-enforceable wherever possible, and open to contributions and forking so that organizations can easily incorporate them into their own corporate coding guidelines. ### FAQ.2: When and where was this work first announced? @@ -21944,11 +21914,11 @@ Because `isocpp` is the Standard C++ Foundation; the committee's repositories ar ### FAQ.8: Will there be a C++98 version of these Guidelines? a C++11 version? -No. These guidelines are about how to best use Standard C++14 (and, if you have an implementation available, the Concepts Technical Specification) and write code assuming you have a modern conforming compiler. +No. These guidelines are about how to best use modern standard C++ and write code assuming you have a modern conforming compiler. ### FAQ.9: Do these guidelines propose new language features? -No. These guidelines are about how to best use Standard C++14 + the Concepts Technical Specification, and they limit themselves to recommending only those features. +No. These guidelines are about how to best use modern Standard C++, and they limit themselves to recommending only those features. ### FAQ.10: What version of Markdown do these guidelines use? @@ -22295,7 +22265,7 @@ Never allow an error to be reported from a destructor, a resource deallocation f void test() { - std::array arr; // this line can std::terminate(!) + std::array arr; // this line can std::terminate() } The behavior of arrays is undefined in the presence of destructors that throw because there is no reasonable rollback behavior that could ever be devised. Just think: What code can the compiler generate for constructing an `arr` where, if the fourth object's constructor throws, the code has to give up and in its cleanup mode tries to call the destructors of the already-constructed objects ... and one or more of those destructors throws? There is no satisfactory answer.