From a4413633ec858f5cd3c12750409e5aaf75c3fae4 Mon Sep 17 00:00:00 2001
From: Daniel Cheng Try to avoid forward declarations of entities
+defined in another project.Forward Declarations
-Try to avoid forward declarations of entities
-defined in another project.
+Inline Functions
@@ -512,8 +512,7 @@ Namespaces
its path. Do not use using-directives (e.g.,
using namespace foo
). Do not use
inline namespaces. For unnamed namespaces, see
-Unnamed Namespaces and
-Static Variables.
+Internal Linkage.
Namespaces subdivide the global scope @@ -674,13 +673,13 @@
When definitions in a .cc
file do not need to be
-referenced outside that file, place them in an unnamed
-namespace or declare them static
. Do not use either
-of these constructs in .h
files.
+referenced outside that file, give them internal linkage by placing
+them in an unnamed namespace or declaring them static
.
+Do not use either of these constructs in .h
files.
All declarations can be given internal linkage by placing them in unnamed @@ -733,7 +732,7 @@
If you define a nonmember function and it is only
needed in its .cc
file, use
-internal linkage to limit
+internal linkage to limit
its scope.
These declarations/deletions can be omitted only if they are obvious: -
These declarations/deletions can be omitted only if they are obvious:
+private
section, like a
struct or an interface-only base class,
then the copyability/movability can be determined by the
@@ -1723,13 +1722,13 @@ Parameters are either inputs to the function, outputs from the
-function, or both. Input parameters should usually be values
+function, or both. Non-optional input parameters should usually be values
or const
references, while non-optional output and
input/output parameters should usually be references (which cannot be null).
Generally, use absl::optional
to represent optional by-value
inputs, and use a const
pointer when the non-optional form would
have used a reference. Use non-const
pointers to represent
-optional outputs.
Avoid defining functions that require a const
reference parameter
@@ -2555,7 +2554,8 @@
reinterpret_cast
to do unsafe conversions of
pointer types to and from integer and other pointer
- types. Use this
+ types,
+ including void*
. Use this
only if you know what you are doing and you understand the aliasing
issues. Also, consider the alternative
absl::bit_cast
.The tradition developed, in C, of using post-increment, even
when the expression value is not used, especially in
-for
loops. Some find post-increment easier
-to read, since the "subject" (i
) precedes
-the "verb" (++
), just like in English.
for
loops.
Use prefix increment/decrement, unless the code explicitly @@ -3294,8 +3292,8 @@
For local variables, you can use type deduction to make the code clearer by eliminating type information that is obvious or irrelevant, so that - the reader can focus on the meaningful parts of the code: -
std::unique_ptr<WidgetWithBellsAndWhistles> widget_ptr = + the reader can focus on the meaningful parts of the code: +std::unique_ptr<WidgetWithBellsAndWhistles> widget_ptr = absl::make_unique<WidgetWithBellsAndWhistles>(arg1, arg2); absl::flat_hash_map<std::string, std::unique_ptr<WidgetWithBellsAndWhistles>>::const_iterator @@ -3311,17 +3309,17 @@Local variable type deduction
type is an iterator, and in many contexts the container type and even the key type aren't relevant, but the type of the values is probably useful. In such situations, it's often possible to define local variables with - explicit types that convey the relevant information: -if (auto it = my_map_.find(key); it != my_map_.end()) { + explicit types that convey the relevant information: +if (auto it = my_map_.find(key); it != my_map_.end()) { WidgetWithBellsAndWhistles& widget = *it->second; // Do stuff with `widget` }- If the type is a template instance, and the parameters are +If the type is a template instance, and the parameters are boilerplate but the template itself is informative, you can use class template argument deduction to suppress the boilerplate. However, cases where this actually provides a meaningful benefit are quite rare. Note that class template argument deduction is also subject to a - separate style rule. + separate style rule.
Do not use
decltype(auto)
if a simpler option will work, because it's a fairly obscure feature, so it has a high cost in code @@ -3373,10 +3371,10 @@Structured bindings
this may also mean the names are less recognizable to your reader than the 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] = ...- As with function parameter comments, this can enable tools to detect if - you get the order of the fields wrong. + same syntax as for function parameter comments: +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.
Class Template Argument Deduction
@@ -3387,21 +3385,21 @@Class Template Argument Deduction
Class template argument deduction (often abbreviated "CTAD") occurs when a variable is declared with a type that names a template, and the template - argument list is not provided (not even empty angle brackets): -
std::array a = {1, 2, 3}; // `a` is a std::array<int, 3>- The compiler deduces the arguments from the initializer using the - template's "deduction guides", which can be explicit or implicit. + argument list is not provided (not even empty angle brackets): +std::array a = {1, 2, 3}; // `a` is a std::array<int, 3>+The compiler deduces the arguments from the initializer using the + template's "deduction guides", which can be explicit or implicit.
Explicit deduction guides look like function declarations with trailing return types, except that there's no leading
auto
, and the function name is the name of the template. For example, the above example - relies on this deduction guide forstd::array
: -namespace std { + relies on this deduction guide forstd::array
: +namespace std { template <class T, class... U> array(T, U...) -> std::array<T, 1 + sizeof...(U)>; }- Constructors in a primary template (as opposed to a template specialization) - also implicitly define deduction guides. +Constructors in a primary template (as opposed to a template specialization) + also implicitly define deduction guides.
When you declare a variable that relies on CTAD, the compiler selects a deduction guide using the rules of constructor overload resolution, @@ -3443,8 +3441,8 @@
Designated Initializers
Designated initializers are a syntax that allows for initializing an - aggregate ("plain old struct") by naming its fields explicitly: -
struct Point { + aggregate ("plain old struct") by naming its fields explicitly: +struct Point { float x = 0.0; float y = 0.0; float z = 0.0; @@ -3455,9 +3453,9 @@- The explicitly listed fields will be initialized as specified, and others +Designated Initializers
.y = 2.0, // z will be 0.0 };The explicitly listed fields will be initialized as specified, and others will be initialized in the same way they would be in a traditional aggregate - initialization expression like
Point{1.0, 2.0}
. + initialization expression likePoint{1.0, 2.0}
.Designated initializers can make for convenient and highly readable @@ -3525,20 +3523,20 @@
Lambda Expressions
A variable capture can also have an explicit initializer, which can be used for capturing move-only variables by value, or for other situations - not handled by ordinary reference or value captures: -
std::unique_ptr<Foo> foo = ...; + not handled by ordinary reference or value captures: +std::unique_ptr<Foo> foo = ...; [foo = std::move(foo)] () { ... }- Such captures (often called "init captures" or "generalized lambda captures") +Such captures (often called "init captures" or "generalized lambda captures") need not actually "capture" anything from the enclosing scope, or even have a name from the enclosing scope; this syntax is a fully general way to define - members of a lambda object: + members of a lambda object:
[foo = std::vector<int>({1, 2, 3})] () { ... }- The type of a capture with an initializer is deduced using the same rules - asauto
. +The type of a capture with an initializer is deduced using the same rules + as
auto
.
As with Boost, some modern C++ extensions encourage coding practices that hamper readability—for example by removing @@ -4013,7 +4012,7 @@
namespace mynamespace { // Used to store field measurements. DataPoint may change from Bar* to some internal type. // Client code should treat it as an opaque pointer. -using DataPoint = foo::Bar*; +using DataPoint = ::foo::Bar*; // A set of measurements. Just an alias for user convenience. using TimeSeries = std::unordered_set<DataPoint, std::hash<DataPoint>, DataPointComparator>; @@ -4319,7 +4318,8 @@Namespace Names
create any nestedstd
namespaces. Prefer unique project identifiers (websearch::index
,websearch::index_util
) -over collision-prone names likewebsearch::util
. +over collision-prone names likewebsearch::util
. Also avoid overly deep nesting + namespaces (TotW #130).For
internal
namespaces, be wary of other code being added to the sameinternal
namespace causing a collision @@ -4327,7 +4327,7 @@Namespace Names
collisions). In such a situation, using the filename to make a unique internal name is helpful (websearch::index::frobber_internal
for use -infrobber.h
) +infrobber.h
).Enumerator Names
@@ -4505,11 +4505,10 @@Function Declarations
Almost every function declaration should have comments immediately preceding it that describe what the function does and how to use it. These comments may be omitted only if the function is simple and -obvious (e.g., simple accessors for obvious properties of the -class). These comments should open with descriptive verbs in the -indicative mood ("Opens the file") rather than verbs in the imperative -("Open the file"). The comment describes the function; it does not -tell the function what to do. In general, these comments do not +obvious (e.g., simple accessors for obvious properties of the class). +Function comments should be written with an implied subject of +This function and should start with the verb phrase; for example, +"Opens the file", rather than "Open the file". In general, these comments do not describe how the function performs its task. Instead, that should be left to comments in the function definition.
@@ -5373,22 +5372,27 @@Pointer and Reference Expressions
* or&
. -When declaring a pointer or reference variable or argument, you may -place the asterisk/ampersand adjacent to either the type or the -variable name:
+When referring to a pointer or reference (variable declarations or definitions, arguments, +return types, template parameters, etc), you may place the space before or after the +asterisk/ampersand. In the trailing-space style, the space is elided in some cases (template +parameters, etc).
// These are fine, space preceding. char *c; const std::string &str; +int *GetPointer(); +std::vector<char *> -// These are fine, space following. +// These are fine, space following (or elided). char* c; const std::string& str; +int* GetPointer(); +std::vector<char*> // Note no space between '*' and '>'You should do this consistently within a single -file, -so, when modifying an existing file, use the style in +file. +When modifying an existing file, use the style in that file.
It is allowed (if unusual) to declare multiple variables in the same