From 93c7bd88c65a897b71a4afd77a305b3420ce5d98 Mon Sep 17 00:00:00 2001 From: Daniel Cheng Date: Tue, 3 May 2022 23:57:01 +0000 Subject: [PATCH] Update C++ style guide. - Reword guidance for definitions of inline functions and templates. - Capitalize the first letter of various comments. - Update command-line flag reference to use absl flags. - Improve braced-initialization example for 64-bit constants. - Delete C++03 guidance for NULL. - Clarify wording around wrapping of string literals. - Use C++17 [[fallthrough]] instead of ABSL_FALLTHROUGH. --- cppguide.html | 157 ++++++++++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 69 deletions(-) diff --git a/cppguide.html b/cppguide.html index 59d3682..07e49e9 100644 --- a/cppguide.html +++ b/cppguide.html @@ -204,19 +204,15 @@ header. Specifically, a header should have header guards and include all other headers it needs.

-

Prefer placing the definitions for template and inline functions in -the same file as their declarations. The definitions of these -constructs must be included into every .cc file that uses -them, or the program may fail to link in some build configurations. If -declarations and definitions are in different files, including the -former should transitively include the latter. Do not move these -definitions to separately included header files (-inl.h); -this practice was common in the past, but is no longer allowed.

- -

As an exception, a template that is explicitly instantiated for -all relevant sets of template arguments, or that is a private -implementation detail of a class, is allowed to be defined in the one -and only .cc file that instantiates the template.

+

When a header declares inline functions or templates that clients of the +header will instantiate, the inline functions and templates must also have +definitions in the header, either directly or in files it includes. Do not move +these definitions to separately included header (-inl.h) files; +this practice was common in the past, but is no longer allowed. When all +instantiations of a template occur in one .cc file, either because +they're +explicit or because the definition is accessible to only +the .cc file, the template definition can be kept in that file.

There are rare cases where a file designed to be included is not self-contained. These are typically intended to be included at unusual @@ -331,7 +327,7 @@ struct D : B {}; #include "b.h" void f(B*); void f(void*); -void test(D* x) { f(x); } // calls f(B*) +void test(D* x) { f(x); } // Calls f(B*) If the #include was replaced with forward decls for B and D, @@ -492,8 +488,8 @@ might look like this:

#include <vector> #include "base/basictypes.h" -#include "base/commandlineflags.h" #include "foo/server/bar.h" +#include "third_party/absl/flags/flag.h"

Exception:

@@ -618,7 +614,7 @@ void MyClass::Foo() {
#include "a.h"
 
-ABSL_FLAG(bool, someflag, false, "dummy flag");
+ABSL_FLAG(bool, someflag, false, "a flag");
 
 namespace mynamespace {
 
@@ -869,26 +865,26 @@ objects with static storage duration if they are trivially destructible.
 Fundamental types (like pointers and int) are trivially
 destructible, as are arrays of trivially destructible types. Note that
 variables marked with constexpr are trivially destructible.

-
const int kNum = 10;  // allowed
+
const int kNum = 10;  // Allowed
 
 struct X { int n; };
-const X kX[] = {{1}, {2}, {3}};  // allowed
+const X kX[] = {{1}, {2}, {3}};  // Allowed
 
 void foo() {
-  static const char* const kMessages[] = {"hello", "world"};  // allowed
+  static const char* const kMessages[] = {"hello", "world"};  // Allowed
 }
 
-// allowed: constexpr guarantees trivial destructor
-constexpr std::array<int, 3> kArray = {{1, 2, 3}};
+// Allowed: constexpr guarantees trivial destructor. +constexpr std::array<int, 3> kArray = {1, 2, 3};
// bad: non-trivial destructor
 const std::string kFoo = "foo";
 
-// bad for the same reason, even though kBar is a reference (the
-// rule also applies to lifetime-extended temporary objects)
+// Bad for the same reason, even though kBar is a reference (the
+// rule also applies to lifetime-extended temporary objects).
 const std::string& kBar = StrCat("a", "b", "c");
 
 void bar() {
-  // bad: non-trivial destructor
+  // Bad: non-trivial destructor.
   static std::map<int, int> kData = {{1, 0}, {2, 0}, {3, 0}};
 }
@@ -902,10 +898,10 @@ applies, though. In particular, a function-local static reference of the form

Initialization is a more complex topic. This is because we must not only consider whether class constructors execute, but we must also consider the evaluation of the initializer:

-
int n = 5;    // fine
-int m = f();  // ? (depends on f)
-Foo x;        // ? (depends on Foo::Foo)
-Bar y = g();  // ? (depends on g and on Bar::Bar)
+
int n = 5;    // Fine
+int m = f();  // ? (Depends on f)
+Foo x;        // ? (Depends on Foo::Foo)
+Bar y = g();  // ? (Depends on g and on Bar::Bar)
 

All but the first statement expose us to indeterminate initialization @@ -918,9 +914,9 @@ constructor call, then the constructor must be specified as constexpr, too:

struct Foo { constexpr Foo(int) {} };
 
-int n = 5;  // fine, 5 is a constant expression
-Foo x(2);   // fine, 2 is a constant expression and the chosen constructor is constexpr
-Foo a[] = { Foo(1), Foo(2), Foo(3) };  // fine
+int n = 5; // Fine, 5 is a constant expression. +Foo x(2); // Fine, 2 is a constant expression and the chosen constructor is constexpr. +Foo a[] = { Foo(1), Foo(2), Foo(3) }; // Fine

Constant initialization is always allowed. Constant initialization of static storage duration variables should be marked with constexpr @@ -936,22 +932,22 @@ dynamic initialization, and reviewed very carefully.

By contrast, the following initializations are problematic:

// Some declarations used below.
-time_t time(time_t*);      // not constexpr!
-int f();                   // not constexpr!
+time_t time(time_t*);      // Not constexpr!
+int f();                   // Not constexpr!
 struct Bar { Bar() {} };
 
 // Problematic initializations.
-time_t m = time(nullptr);  // initializing expression not a constant expression
-Foo y(f());                // ditto
-Bar b;                     // chosen constructor Bar::Bar() not constexpr
+time_t m = time(nullptr); // Initializing expression not a constant expression. +Foo y(f()); // Ditto +Bar b; // Chosen constructor Bar::Bar() not constexpr.

Dynamic initialization of nonlocal variables is discouraged, and in general it is forbidden. However, we do permit it if no aspect of the program depends on the sequencing of this initialization with respect to all other initializations. Under those restrictions, the ordering of the initialization does not make an observable difference. For example:

-
int p = getpid();  // allowed, as long as no other static variable
-                   // uses p in its own initialization
+
int p = getpid();  // Allowed, as long as no other static variable
+                   // uses p in its own initialization.

Dynamic initialization of static local variables is allowed (and common).

@@ -969,19 +965,20 @@ does not make an observable difference. For example:

  • Maps, sets, and other dynamic containers: if you require a static, fixed collection, such as a set to search against or a lookup table, you cannot use the dynamic containers from the standard library as a static variable, - since they have non-trivial destructors. Instead, consider a simple array of - trivial types, e.g., an array of arrays of ints (for a "map from int to + since they have non-trivial destructors. Instead, consider + + + a simple array of trivial types, e.g., an array of arrays of ints (for a "map from int to int"), or an array of pairs (e.g., pairs of int and const char*). For small collections, linear search is entirely sufficient (and efficient, due to memory locality); consider using the facilities from - absl/algorithm/container.h - - for the standard operations. If necessary, keep the collection in sorted - order and use a binary search algorithm. If you do really prefer a dynamic - container from the standard library, consider using a function-local static - pointer, as described below.
  • + order and use a binary search algorithm. + + If you do really prefer a dynamic container from the standard library, consider using + a function-local static pointer, as described below + .
  • Smart pointers (unique_ptr, shared_ptr): smart pointers execute cleanup during destruction and are therefore forbidden. Consider whether your use case fits into one of the other patterns described @@ -991,8 +988,8 @@ does not make an observable difference. For example:

    a type that you need to define yourself, give the type a trivial destructor and a constexpr constructor.
  • If all else fails, you can create an object dynamically and never delete - it by using a function-local static pointer or reference (e.g., static - const auto& impl = *new T(args...);).
  • + it by using a function-local static pointer or reference (e.g., + static const auto& impl = *new T(args...);).

    thread_local Variables

    @@ -1705,11 +1702,27 @@ sections that would be empty.

    Within each section, prefer grouping similar kinds of declarations together, and prefer the -following order: types and type aliases (typedef, -using, enum, nested structs and classes), -static constants, factory functions, constructors and assignment -operators, destructor, all other member and friend functions, -data members.

    +following order:

    + +
      +
    1. Types and type aliases (typedef, using, + enum, nested structs and classes)
    2. + +
    3. Static constants
    4. + +
    5. Factory functions
    6. + +
    7. Constructors and assignment operators
    8. + +
    9. Destructor
    10. + +
    11. + All other functions (static and non-static member + functions, and friend functions) +
    12. + +
    13. Data members (static and non-static)
    14. +

    Do not put large method definitions inline in the class definition. Usually, only trivial or @@ -2998,7 +3011,7 @@ problems of printing, comparisons, and structure alignment.

    Use braced-initialization as needed to create 64-bit constants. For example:

    int64_t my_value{0x123456789};
    -uint64_t my_mask{3ULL << 48};
    +uint64_t my_mask{uint64_t{3} << 48};
     
    @@ -3077,6 +3090,8 @@ possible:

  • Prefer not using ## to generate function/class/variable names.
  • + +

    Exporting macros from headers (i.e., defining them in a header @@ -3094,12 +3109,6 @@ not the 0 literal).

    For pointers (address values), use nullptr, as this provides type-safety.

    -

    For C++03 projects, prefer NULL to 0. While the -values are equivalent, NULL looks more like a pointer to the -reader, and some C++ compilers provide special definitions of NULL -which enable them to give useful warnings. Never use NULL for -numeric (integer or floating-point) values.

    -

    Use '\0' for the null character. Using the correct type makes the code more readable.

    @@ -3367,7 +3376,7 @@ std::array numbers = {4, 8, 15, 16, 23, 42}; field names. We recommend using a comment to indicate the name of the underlying field, if it doesn't match the name of the binding, using the same syntax as for function parameter comments:

    -
    auto [/*field_name1=*/ bound_name1, /*field_name2=*/ bound_name2] = ...
    +
    auto [/*field_name1=*/bound_name1, /*field_name2=*/bound_name2] = ...

    As with function parameter comments, this can enable tools to detect if you get the order of the fields wrong.

    @@ -4759,8 +4768,20 @@ can easily show longer lines.

    readability, ease of cut and paste or auto-linking -- e.g., if a line contains an example command or a literal URL longer than 80 characters. -
  • a raw-string literal with content that exceeds 80 characters. Except for - test code, such literals should appear near the top of a file.
  • +
  • a string literal that cannot easily be wrapped at 80 columns. + This may be because it contains URIs or other semantically-critical pieces, + or because the literal contains an embedded language, or a multiline + literal whose newlines are significant like help messages. + In these cases, breaking up the literal would + reduce readability, searchability, ability to click links, etc. Except for + test code, such literals should appear at namespace scope near the top of a + file. If a tool like Clang-Format doesn't recognize the unsplittable content, + + disable the tool around the content as necessary. +

    + (We must balance between usability/searchability of such literals and the + readability of the code around them.) +
  • an include statement.
  • @@ -5200,10 +5221,8 @@ case should never execute, treat this as an error. For example:

    Fall-through from one case label to another must be annotated using the -ABSL_FALLTHROUGH_INTENDED; macro (defined in - -absl/base/macros.h). -ABSL_FALLTHROUGH_INTENDED; should be placed at a +[[fallthrough]]; attribute. +[[fallthrough]]; should be placed at a point of execution where a fall-through to the next case label occurs. A common exception is consecutive case labels without intervening code, in which case no @@ -5214,14 +5233,14 @@ annotation is needed.

    case 43: if (dont_be_picky) { // Use this instead of or along with annotations in comments. - ABSL_FALLTHROUGH_INTENDED; + [[fallthrough]]; } else { CloseButNoCigar(); break; } case 42: DoSomethingSpecial(); - ABSL_FALLTHROUGH_INTENDED; + [[fallthrough]]; default: DoSomethingGeneric(); break;