From 7dce4d9c7ad5364dc206accc517c1acd25303ae0 Mon Sep 17 00:00:00 2001 From: Sergey Zubkov Date: Thu, 12 Oct 2023 17:05:22 -0400 Subject: [PATCH] update from master --- CppCoreGuidelines.md | 103 ++++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 30 deletions(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index f6c3897..5d35d3b 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1,6 +1,6 @@ # C++ Core Guidelines -September 29, 2023 +October 12, 2023 Editors: @@ -2310,8 +2310,8 @@ So, we write a class public: enum Opt { from_line = 1 }; Istream() { } - Istream(zstring p) : owned{true}, inp{new ifstream{p}} {} // read from file - Istream(zstring p, Opt) : owned{true}, inp{new istringstream{p}} {} // read from command line + Istream(czstring p) : owned{true}, inp{new ifstream{p}} {} // read from file + Istream(czstring p, Opt) : owned{true}, inp{new istringstream{p}} {} // read from command line ~Istream() { if (owned) delete inp; } operator istream&() { return *inp; } private: @@ -3188,7 +3188,7 @@ A `struct` of many (individually cheap-to-move) elements might be in aggregate e ##### Exceptions * For non-concrete 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 reference to non-`const` target object to fill (to be used as an out-parameter). +* 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 reference to non-`const` target object to fill (to be used as an out-parameter). * To reuse an object that carries capacity (e.g., `std::string`, `std::vector`) across multiple calls to the function in an inner loop: [treat it as an in/out parameter and pass by reference](#Rf-out-multi). ##### Example @@ -3301,7 +3301,7 @@ To compare, if we passed out all values as return values, we would something lik return {in, move(s)}; } - for (auto p = get_string(cin); p.first; ) { + for (auto p = get_string(cin); p.first; p.second = get_string(p.first).second) { // do something with p.second } @@ -6438,9 +6438,11 @@ A non-throwing move will be used more efficiently by standard-library and langua public: Vector(Vector&& a) noexcept :elem{a.elem}, sz{a.sz} { a.elem = nullptr; a.sz = 0; } Vector& operator=(Vector&& a) noexcept { - delete elem; - elem = a.elem; a.elem = nullptr; - sz = a.sz; a.sz = 0; + if (&a != this) { + delete elem; + elem = a.elem; a.elem = nullptr; + sz = a.sz; a.sz = 0; + } return *this; } // ... @@ -10858,7 +10860,7 @@ The *always initialize* rule is a style rule aimed to improve maintainability as Here is an example that is often considered to demonstrate the need for a more relaxed rule for initialization - widget i; // "widget" a type that's expensive to initialize, possibly a large POD + widget i; // "widget" a type that's expensive to initialize, possibly a large trivial type widget j; if (cond) { // bad: i and j are initialized "late" @@ -11093,7 +11095,8 @@ For containers, there is a tradition for using `{...}` for a list of elements an int x {7.9}; // error: narrowing int y = 7.9; // OK: y becomes 7. Hope for a compiler warning - int z = gsl::narrow_cast(7.9); // OK: you asked for it + int z {gsl::narrow_cast(7.9)}; // OK: you asked for it + auto zz = gsl::narrow_cast(7.9); // OK: you asked for it ##### Note @@ -11365,8 +11368,6 @@ Also, `#` and `##` encourages the definition and use of macros: There are workarounds for low-level string manipulation using macros. For example: - string s = "asdf" "lkjh"; // ordinary string literal concatenation - enum E { a, b }; template @@ -11378,9 +11379,10 @@ There are workarounds for low-level string manipulation using macros. For exampl } } - void f(int x, int y) + void f() { - string sx = stringify(); + string s1 = stringify(); + string s2 = stringify(); // ... } @@ -16856,19 +16858,28 @@ Prevents accidental or hard-to-notice change of value. for (int i : c) cout << i << '\n'; // BAD: just reading -##### Exception +##### Exceptions + +A local variable that is returned by value and is cheaper to move than copy should not be declared `const` +because it can force an unnecessary copy. + + std::vector f(int i) + { + std::vector v{ i, i, i }; // const not needed + return v; + } Function parameters passed by value are rarely mutated, but also rarely declared `const`. To avoid confusion and lots of false positives, don't enforce this rule for function parameters. - void f(const char* const p); // pedantic void g(const int i) { ... } // pedantic Note that a function parameter is a local variable so changes to it are local. ##### Enforcement -* Flag non-`const` variables that are not modified (except for parameters to avoid many false positives) +* Flag non-`const` variables that are not modified (except for parameters to avoid many false positives +and returned local variables) ### Con.2: By default, make member functions `const` @@ -18437,21 +18448,22 @@ Specialization offers a powerful mechanism for providing alternative implementat This is a simplified version of `std::copy` (ignoring the possibility of non-contiguous sequences) - struct pod_tag {}; - struct non_pod_tag {}; + struct trivially_copyable_tag {}; + struct non_trivially_copyable_tag {}; - template struct copy_trait { using tag = non_pod_tag; }; // T is not "plain old data" - - template<> struct copy_trait { using tag = pod_tag; }; // int is "plain old data" + // T is not trivially copyable + template struct copy_trait { using tag = non_trivially_copyable_tag; }; + // int is trivially copyable + template<> struct copy_trait { using tag = trivially_copyable_tag; }; template - Out copy_helper(Iter first, Iter last, Iter out, pod_tag) + Out copy_helper(Iter first, Iter last, Iter out, trivially_copyable_tag) { // use memmove } template - Out copy_helper(Iter first, Iter last, Iter out, non_pod_tag) + Out copy_helper(Iter first, Iter last, Iter out, non_trivially_copyable_tag) { // use loop calling copy constructors } @@ -18459,7 +18471,8 @@ This is a simplified version of `std::copy` (ignoring the possibility of non-con template Out copy(Iter first, Iter last, Iter out) { - return copy_helper(first, last, out, typename copy_trait>::tag{}) + using tag_type = typename copy_trait>; + return copy_helper(first, last, out, tag_type{}) } void use(vector& vi, vector& vi2, vector& vs, vector& vs2) @@ -18472,10 +18485,10 @@ This is a general and powerful technique for compile-time algorithm selection. ##### Note -When `concept`s become widely available such alternatives can be distinguished directly: +With C++20 constraints, such alternatives can be distinguished directly: template - requires Pod> + requires std::is_trivially_copyable_v> Out copy_helper(In, first, In last, Out out) { // use memmove @@ -19208,6 +19221,7 @@ Source file rule summary: * [SF.10: Avoid dependencies on implicitly `#include`d names](#Rs-implicit) * [SF.11: Header files should be self-contained](#Rs-contained) * [SF.12: Prefer the quoted form of `#include` for files relative to the including file and the angle bracket form everywhere else](#Rs-incform) +* [SF.13: Use portable header identifiers in `#include` statements](#Rs-portable-header-id) * [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) @@ -19618,7 +19632,7 @@ Nevertheless, the guidance is to use the quoted form for including files that ex #include // From the standard library, requires the <> form #include // A file that is not locally relative, included from another library; use the <> form #include "foo.h" // A file locally relative to foo.cpp in the same project, use the "" form - #include "foo_utils/utils.h" // A file locally relative to foo.cpp in the same project, use the "" form + #include "util/util.h" // A file locally relative to foo.cpp in the same project, use the "" form #include // A file in the same project located via a search path, use the <> form ##### Note @@ -19631,6 +19645,34 @@ Library creators should put their headers in a folder and have clients include t A test should identify whether headers referenced via `""` could be referenced with `<>`. +### SF.13: Use portable header identifiers in `#include` statements + +##### Reason + +The [standard](http://eel.is/c++draft/cpp.include) does not specify how compilers uniquely locate headers from an identifier in an `#include` directive, nor does it specify what constitutes uniqueness. For example, whether the implementation considers the identifiers to be case-sensitive, or whether the identifiers are file system paths to a header file, and if so, how a hierarchical file system path is delimited. + +To maximize the portability of `#include` directives across compilers, guidance is to: + +* use case-sensitivity for the header identifier, matching how the header is defined by the standard, specification, implementation, or file that provides the header. +* when the header identifier is a hierarchical file path, use forward-slash `/` to delimit path components as this is the most widely-accepted path-delimiting character. + +##### Example + + // good examples + #include + #include + #include "util/util.h" + + // bad examples + #include // bad: the standard library defines a header identified as , not + #include // bad: the standard library defines a header identified as , not + #include "Util/Util.H" // bad: the header file exists on the file system as "util/util.h" + #include "util\util.h" // bad: may not work if the implementation interprets `\u` as an escape sequence, or where '\' is not a valid path separator + +##### Enforcement + +It is only possible to enforce on implementations where header identifiers are case-sensitive and which only support `/` as a file path delimiter. + ### SF.20: Use `namespace`s to express logical structure ##### Reason @@ -21594,7 +21636,8 @@ ISO Standard, use lower case only and digits, separate words with underscores: * `vector` * `my_map` -Avoid double underscores `__`. +Avoid identifier names that contain double underscores `__` or that start with an underscore followed by a capital letter (e.g., `_Throws`). +Such identifiers are reserved for the C++ implementation. ##### Example @@ -21643,7 +21686,7 @@ To avoid confusing macros with names that obey scope and type rules. ##### Note -This rule applies to non-macro symbolic constants: +In particular, this avoids confusing macros with non-macro symbolic constants (see also [Enum.5: Don't use `ALL_CAPS` for enumerators](#Renum-caps)) enum bad { BAD, WORSE, HORRIBLE }; // BAD