From f15e633de5b716a966504e0652fb0c85c2f4f4fb Mon Sep 17 00:00:00 2001
From: Titus Winters C++ is the main development language used by
+ C++ is one of the main development languages used by
many of Google's open-source projects. As every C++
programmer knows, the language has many powerful features, but
this power brings with it complexity, which in turn can make
@@ -179,36 +179,37 @@ pitfalls of using header files. Header files should be self-contained and end in Header files should be self-contained (compile on their own) and
+end in All header files should be self-contained. In other
-words, users and refactoring tools should not have to adhere to special
-conditions in order to include the header. Specifically, a
-header should have header guards,
-should include all other headers it needs, and should not require any
-particular symbols to be defined. All header files should be self-contained. Users and refactoring
+tools should not have to adhere to special conditions to include the
+header. Specifically, a header should
+have header guards and include all
+other headers it needs. There are rare cases where a file is not meant to be self-contained, but
-instead is meant to be textually included at a specific point in the code.
-Examples are files that need to be included multiple times or
-platform-specific extensions that essentially are part of other headers. Such
-files should use the file extension If a template or inline function is declared in a If a template or inline function is declared in a 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 As an exception, a function template that is explicitly
-instantiated for all relevant sets of template arguments, or
-that is a private member of a class, may
-be defined in the only There are rare cases where a file is not meant to be
+self-contained, but it is meant to be included at a specific point in
+the code. Examples are files that need to be included multiple times
+or platform-specific implementation details that are essentially part
+of other self-contained headers. Such files should use the file
+extension Background
-Self-contained Headers
.h
. Files that
-are meant for textual inclusion, but are not headers, should end in
-.inc
. Separate -inl.h
headers are disallowed..h
. Non-header files that are meant for inclusion
+should end in .inc
. .inc
..h
+file, that same header should provide its definition. 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. Do not move these definitions to separately included
+header files (-inl.h
); this practice was common in the
+past, but is no longer allowed..h
file,
-define it in that same file. 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. Do not move these
-definitions to separate -inl.h
files..cc
file that instantiates the template..cc
file that
-instantiates the template..inc
.
With few exceptions, place code in a namespace. Namespaces
should have unique names based on the project name, and possibly
its path. Unnamed namespaces in .cc
files are
-encouraged. Do not use using-directives. Do not use
+encouraged. Do not use using-directives (e.g.
+using namespace foo
). Do not use
inline namespaces.
Sometimes it is useful, or even necessary, to define a +
Sometimes it is useful to define a function not bound to a class instance. Such a function can be either a static member or a nonmember function. Nonmember functions should not depend on external @@ -758,7 +760,7 @@ class FooBar { } -
If you must define a nonmember function and it is only +
If you define a nonmember function and it is only
needed in its Support copying and/or moving if it makes sense for your type.
-Otherwise, disable the implicitly generated special
-functions that perform copies and moves. Support copying and/or moving if these operations are clear and meaningful
+for your type. Otherwise, disable the implicitly generated special functions
+that perform copies and moves.
+.cc
file, use an unnamed
namespace or
static
linkage (eg static int Foo()
@@ -1097,10 +1099,10 @@ also omit
explicit
, in order to support copy-initialization
Copyable and Movable Types
Objects of copyable and movable types can be passed and returned -by value, which makes APIs simpler, safer, and more general. -Unlike when passing pointers or references, there's no risk of -confusion over ownership, lifetime, mutability, and similar -issues, and no need to specify them in the contract. It also -prevents non-local interactions between the client and the -implementation, which makes them easier to understand and -maintain. Such objects can be used with generic -APIs that require pass-by-value, such as most containers.
+Objects of copyable and movable types can be passed and returned by value, +which makes APIs simpler, safer, and more general. Unlike when passing objects +by pointer or reference, there's no risk of confusion over ownership, +lifetime, mutability, and similar issues, and no need to specify them in the +contract. It also prevents non-local interactions between the client and the +implementation, which makes them easier to understand, maintain, and optimize by +the compiler. Further, such objects can be used with generic APIs that +require pass-by-value, such as most containers, and they allow for additional +flexibility in e.g., type composition.
Copy/move constructors and assignment operators are usually easier to define correctly than alternatives @@ -1151,54 +1153,39 @@ in some cases.
Many types do not need to be copyable, and providing copy -operations for them can be confusing, nonsensical, or outright -incorrect. Copy/assigment operations for base class types are -hazardous, because use of them can lead to -object -slicing. Defaulted or carelessly-implemented copy operations -can be incorrect, and the resulting bugs can be confusing and -difficult to diagnose.
+Some types do not need to be copyable, and providing copy
+operations for such types can be confusing, nonsensical, or outright
+incorrect. Types representing singleton objects (Registerer
),
+objects tied to a specific scope (Cleanup
), or closely coupled to
+object identity (Mutex
) cannot be copied meaningfully.
+Copy operations for base class types that are to be used
+polymorphically are hazardous, because use of them can lead to
+object slicing.
+Defaulted or carelessly-implemented copy operations can be incorrect, and the
+resulting bugs can be confusing and difficult to diagnose.
Copy constructors are invoked implicitly, which makes the -invocation easy to miss. This may cause confusion, particularly -for programmers used to languages where pass-by-reference is -conventional or mandatory. It may also encourage excessive -copying, which can cause performance problems.
- - +invocation easy to miss. This may cause confusion for programmers used to +languages where pass-by-reference is conventional or mandatory. It may also +encourage excessive copying, which can cause performance problems.Make your type copyable/movable if it will be useful, and if it makes sense -in the context of the rest of the API. As a rule of thumb, if the behavior -(including computational complexity) of a copy isn't immediately obvious to -users of your type, your type shouldn't be copyable. If you define a copy or -move constructor, define the corresponding assignment operator, and vice-versa. -If your type is copyable, do not define move operations unless they are -significantly more efficient than the corresponding copy operations. If your +
Provide the copy and move operations if their meaning is clear to a casual +user and the copying/moving does not incur unexpected costs. If you define a +copy or move constructor, define the corresponding assignment operator, and +vice-versa. If your type is copyable, do not define move operations unless they +are significantly more efficient than the corresponding copy operations. If your type is not copyable, but the correctness of a move is obvious to users of the type, you may make the type move-only by defining both of the move operations.
If your type provides copy operations, it is recommended that you design
-your class so that the default implementation of those operations is correct
-and that you explicitly define them with = default
. Remember to
-review the correctness of any defaulted operations as you would any other
-code.
class Foo { // Copyable and movable type. - public: - Foo(Foo&& other) = default; - Foo(const Foo& other) = default; - Foo& operator=(Foo&& other) = default; - Foo& operator=(const Foo& other) = default; - - private: - string field_; -}; -+your class so that the default implementation of those operations is correct. +Remember to review the correctness of any defaulted operations as you would any +other code, and to document that your class is copyable and/or cheaply movable +if that's an API guarantee.
class Foo { public: @@ -1218,93 +1205,11 @@ copyable, provide a public virtualClone()
method, and a protected copy constructor that derived classes can use to implement it. -If you do not want to support copy/move operations on -your type, explicitly disable them using
= delete
or -whatever -other mechanism your project uses. +If you do not want to support copy/move operations on your type, +explicitly disable them using
-= delete
in +thepublic:
section.
Use delegating and inheriting -constructors when they reduce code duplication.
-Delegating and inheriting constructors are two -different features, both introduced in C++11, for -reducing code duplication in constructors. Delegating -constructors allow one of a class's constructors to -forward work to one of the class's other constructors, -using a special variant of the initialization list -syntax. For example:
- -X::X(const string& name) : name_(name) { - ... -} - -X::X() : X("") {} -- -
Inheriting constructors allow a derived class to have -its base class's constructors available directly, just as -with any of the base class's other member functions, -instead of having to redeclare them. This is especially -useful if the base has multiple constructors. For -example:
- -class Base { - public: - Base(); - Base(int n); - Base(const string& s); - ... -}; - -class Derived : public Base { - public: - using Base::Base; // Base's constructors are redeclared here. -}; -- -
This is especially useful when Derived
's
-constructors don't have to do anything more than calling
-Base
's constructors.
Delegating and inheriting constructors reduce -verbosity and boilerplate, which can improve -readability.
- -Delegating constructors are familiar to Java -programmers.
-It's possible to approximate the behavior of -delegating constructors by using a helper function.
- -Inheriting constructors may be confusing if a derived -class introduces new member variables, since the base -class constructor doesn't know about them.
-Use delegating and inheriting constructors when they reduce -boilerplate and improve readability. -Be cautious about inheriting constructors when your derived class has -new member variables. Inheriting constructors may still be appropriate -in that case if you can use in-class member initialization -for the derived class's member variables.
-static const
data
members)std::auto_ptr
. Instead, use
std::unique_ptr
.
-
-
@@ -2628,9 +2531,10 @@ them.
explicit type conversion is necessary.
int64{x}
). This is the safest approach because code
+ will not compile if conversion can result in information loss. The
+ syntax is also concise.<<
operators interferes with
internationalization, because it bakes word order into the
-code, and streams' support for localization is
+code, and streams' support for localization is
flawed.Be very cautious with macros. Prefer inline functions,
-enums, and const
variables to macros.
const
variables to macros. Don't
+use macros to define pieces of a C++ API.
const
variables to macros.
the code the compiler sees. This can introduce unexpected
behavior, especially since macros have global scope.
+The problems introduced by macros are especially severe +when they are used to define pieces of a C++ API, +and still more so for public APIs. Every error message from +the compiler when developers incorrectly use that interface +now must explain how the macros formed the interface. +Refactoring and analysis tools have a dramatically harder +time updating the interface. As a consequence, we +specifically disallow using macros in this way. +For example, avoid patterns like:
+ +class WOMBAT_TYPE(Foo) { + // ... + + public: + EXPAND_PUBLIC_WOMBAT_API(Foo) + + EXPAND_WOMBAT_COMPARISONS(Foo, ==, <) +}; ++
Luckily, macros are not nearly as necessary in C++ as they are in C. Instead of using a macro to inline performance-critical code, use an inline function. @@ -3254,7 +3179,10 @@ lower-level libraries. And some of their special features (like stringifying, concatenation, and so forth) are not available through the language proper. But before using a macro, consider carefully whether there's a non-macro way -to achieve the same result.
+to achieve the same result. If you need to use a macro to +define an interface, contact +your project leads to request +a waiver of this rule.The following usage pattern will avoid many problems with macros; if you use macros, follow it whenever @@ -3347,96 +3275,47 @@ memset(&data, 0, sizeof(data));
Use auto
to avoid type names that are just
-clutter. Continue to use manifest type declarations when it
-helps readability, and never use auto
for
-anything but local variables.
Use auto
to avoid type names that are noisy, obvious,
+or unimportant - cases where the type doesn't aid in clarity for the
+reader. Continue to use manifest type declarations when it helps
+readability, and never use auto
for anything but local
+variables.
In C++11, a variable whose type is given as auto
-will be given a type that matches that of the expression used to
-initialize it. You can use auto
either to
-initialize a variable by copying, or to bind a
-reference.
vector<string> v; -... -auto s1 = v[0]; // Makes a copy of v[0]. -const auto& s2 = v[0]; // s2 is a reference to v[0]. --
The auto
keyword is also used in an
-unrelated C++11 feature: it's part of the syntax for a
-new kind of function declaration with a
-trailing return type.
C++ type names can sometimes be long and cumbersome, -especially when they involve templates or namespaces. In -a statement like:
- -sparse_hash_map<string, int>::iterator iter = m.find(val); -+
+
the return type is hard to read, and obscures the -primary purpose of the statement. Changing it to:
- -auto iter = m.find(val); -- -
makes it more readable.
- -Without auto
we are sometimes forced to
-write a type name twice in the same expression, adding no
-value for the reader, as in:
diagnostics::ErrorStatus* status = new diagnostics::ErrorStatus("xyz"); -- -
Using auto
makes it easier to use
-intermediate variables when appropriate, by reducing the
-burden of writing their types explicitly.
Sometimes code is clearer when types are manifest, especially when a variable's initialization depends on -things that were declared far away. In an expression +things that were declared far away. In expressions like:
- -auto i = x.Lookup(key); +auto foo = x.add_foo(); +auto i = y.Find(key);-it may not be obvious what
+i
's type is, -ifx
was declared hundreds of lines earlier.It may not be obvious what the resulting types are if the type +of
y
isn't very well known, or ify
was +declared many lines earlier.Programmers have to understand the difference between
-auto
andconst auto&
or they'll get copies when they didn't mean to.The interaction between
- -auto
and C++11 -brace-initialization can be confusing. The exact -rules have been in flux, and compilers don't all -implement the final rules yet. -The declarations:auto x{3}; -auto y = {3}; -- -mean different things — -
-x
is anint
, while -y
is astd::initializer_list<int>
. -The same applies to other normally-invisible proxy types. -If an
auto
variable is used as part of an interface, e.g. as a constant in a header, then a programmer might change its type while only intending to @@ -3446,12 +3325,47 @@ than intended.-+
auto
is permitted, for local variables -only. Do not useauto
for file-scope or -namespace-scope variables, or for class members. Never -initialize anauto
-typed variable with -a braced initializer list. -+ +
auto
is permitted, for local variables only, when it +increases readability, particularly as described below. Do not +useauto
for file-scope or namespace-scope variables, or +for class members. Never initialize anauto
-typed +variable with a braced initializer list.Specific cases where
auto
is allowed or encouraged: +
find
, begin
, or end
for
+instance).new
+commonly falls into this category, as does use of auto
in
+a range-based loop over a container whose type is spelled out
+nearby.pair<KeyType,ValueType>
whereas it is actually
+pair<const KeyType,ValueType>
). This is
+particularly well paired with local key
+and value
aliases for .first
+and .second
(often const-ref).
+for (const auto& item : some_map) { + const KeyType& key = item.first; + const ValType& value = item.second; + // The rest of the loop can now just refer to key and value, + // a reader can see the types in question, and we've avoided + // the too-common case of extra copies in this iteration. +} ++
Use lambda expressions where appropriate. Avoid
-default lambda captures when capturing this
or
-if the lambda will escape the current scope.
Use lambda expressions where appropriate. Prefer explicit captures +when the lambda will escape the current scope.
this
if any members are used. The default capture must come
-first and be one of &
for capture by reference or
-=
for capture by value, followed by any explicit captures which
-differ from the default:
+They further allow capturing variables from the enclosing scope either +explicitly by name, or implicitly using a default capture. Explicit captures +require each variable to be listed, as +either a value or reference capture:
int weight = 3; int sum = 0; -// Captures `weight` (implicitly) by value and `sum` by reference. -std::for_each(v.begin(), v.end(), [=, &sum](int x) { +// Captures `weight` by value and `sum` by reference. +std::for_each(v.begin(), v.end(), [weight, &sum](int x) { sum += weight * x; });+ +Default captures implicitly capture any variable referenced in the +lambda body, including
this
if any members are used:
+
+const std::vector<int> lookup_table = ...; +std::vector<int> indices = ...; +// Captures `lookup_table` by reference, sorts `indices` by the value +// of the associated element in `lookup_table`. +std::sort(indices.begin(), indices.end(), [&](int a, int b) { + return lookup_table[a] < lookup_table[b]; +}); ++
Lambdas were introduced in C++11 along with a set of utilities
for working with function objects, such as the polymorphic
wrapper std::function
.
@@ -3614,19 +3537,20 @@ wrapper std::function
.
this
.
- std::function
.
this
or if the lambda will escape the
-current scope. For example, instead of:
+{ Foo foo; ... @@ -3662,6 +3584,15 @@ prefer to write: // reference.
Nonstandard extensions to C++ may not be used unless otherwise specified.
+Compilers support various extensions that are not part of standard C++. Such
+ extensions include GCC's __attribute__
, intrinsic functions such
+ as __builtin_prefetch
, designated initializers (e.g.
+ Foo f = {.field = 3}
), inline assembly, __COUNTER__
,
+ __PRETTY_FUNCTION__
, compound statement expressions (e.g.
+ foo = ({ int x; Bar(&x); x })
, and the a?:b
+ syntax.
Do not use nonstandard extensions. You may use portability wrappers that + are implemented using nonstandard extensions, so long as those wrappers + + are provided by a designated project-wide + portability header.
+Names should be descriptive; eschew abbreviation.
+Names should be descriptive; avoid abbreviation.
There are no special requirements for global
-variables, which should be rare in any case, but if you
-use one, consider prefixing it with g_
or
-some other marker to easily distinguish it from local
-variables.
Naming conventions for nested namespaces are at the discretion
-of the team owning the enclosing namespace. If a nested namespace
-is used to group related functions in a single header file, consider basing
-its name on the header name, e.g. namespace foo::bar
-for functions defined in the header file bar.h
.
-If the nested namespace is being used to distinguish implementation
-details from user-facing declarations, one common choice is
-internal
.
Avoid nested namespaces that match well-known top-level
+namespaces. Collisions between namespace names can lead to surprising
+build breaks because of name lookup rules. In particular, do not
+create any nested std
namespaces. Prefer unique project
+identifiers
+(websearch::index
, websearch::index_util
)
+over collision-prone names like websearch::util
.
For internal
namespaces, be wary of other code being
+added to the same internal
namespace causing a collision
+(internal helpers within a team tend to be related and may lead to
+collisions). In such a situation, using the filename to make a unique
+internal name is helpful
+(websearch::index::frobber_internal
for use
+in frobber.h
)
Enumerators should be named either like +
Enumerators (for both scoped and unscoped enums) should be named either like
constants or like
macros: either kEnumName
or
ENUM_NAME
.
Start each file with license -boilerplate, followed by a description of its -contents.
+Start each file with license boilerplate.
+ +File comments describe the contents of a file. If a file declares, +implements, or tests exactly one abstraction that is documented by a comment +at the point of declaration, file comments are not required. All other files +must have file comments.
+Every file should have a comment at the top describing -its contents, unless the specific conditions described below apply.
- -A file-level comment in a .h
should broadly describe
-the contents of the file. If the file declares multiple abstractions, the
-file-level comment should document how they are related. A 1 or 2 sentence
-file-level comment may be sufficient. The detailed documentation about
-individual abstractions belongs with those abstractions, not at the file
-level.
If a .h
declares multiple abstractions, the file-level comment
+should broadly describe the contents of the file, and how the abstractions are
+related. A 1 or 2 sentence file-level comment may be sufficient. The detailed
+documentation about individual abstractions belongs with those abstractions,
+not at the file level.
Do not duplicate comments in both the .h
and the
.cc
. Duplicated comments diverge.
A file-level comment may be omitted if the file declares, implements, or -tests exactly one abstraction that is documented by a comment at the point -of declaration.
- -
Every class definition should have an accompanying comment -that describes what it is for and how it should be used.
+Every non-obvious class declaration should have an accompanying +comment that describes what it is for and how it should be used.
The class comment is often a good place for a small example code snippet demonstrating a simple and focused usage of the class.
+When sufficiently separated (e.g. .h
and .cc
+files), comments describing the use of the class should go together with its
+interface definition; comments about the class operation and implementation
+should accompany the implementation of the class's methods.
Declaration comments describe use of the function; comments -at the definition of a function describe operation.
+Declaration comments describe use of the function (when it is +non-obvious); comments at the definition of a function describe +operation.
Every function declaration should have comments -immediately preceding it that describe what the function -does and how to use it. These comments should be -descriptive ("Opens the file") rather than imperative -("Open the file"); the comment describes the function, it -does not tell the function what to do. In general, these -comments do not describe how the function performs its -task. Instead, that should be left to comments in the -function definition.
+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 be descriptive ("Opens the file") +rather than imperative ("Open the file"); the comment describes the +function, it does not tell the function what to do. In general, these +comments do not describe how the function performs its task. Instead, +that should be left to comments in the function definition.
Types of things to mention in comments at the function declaration:
@@ -4733,25 +4715,28 @@ is used for. In certain cases, more comments are required.Each class data member (also called an instance -variable or member variable) should have a comment -describing what it is used for. If the variable can take -sentinel values with special meanings, such as a null -pointer or -1, document this. For example:
+The purpose of each class data member (also called an instance
+variable or member variable) must be clear. If there are any
+invariants (special values, relationships between members, lifetime
+requirements) not clearly expressed by the type and name, they must be
+commented. However, if the type and name suffice (int
+num_events_;
), no comment is needed.
In particular, add comments to describe the existence and meaning +of sentinel values, such as nullptr or -1, when they are not +obvious. For example:
private: - // Keeps track of the total number of entries in the table. - // Used to ensure we do not go over the limit. -1 means + // Used to bounds-check table accesses. -1 means // that we don't yet know how many entries the table has. int num_total_entries_;
As with data members, all global variables should have -a comment describing what they are and what they are used -for. For example:
+All global variables should have a comment describing what they +are, what they are used for, and (if unclear) why it needs to be +global. For example:
// The total number of tests cases that we run through in this regression test. const int kNumTestCases = 6; @@ -5226,10 +5211,10 @@ function call.+class Foo { public: - Foo(Foo&&) = default; - Foo(const Foo&) = default; - Foo& operator=(Foo&&) = default; - Foo& operator=(const Foo&) = default; + Foo(Foo&&); + Foo(const Foo&); + Foo& operator=(Foo&&); + Foo& operator=(const Foo&); };@@ -5254,6 +5239,12 @@ void Circle::Rotate(double /*radians*/) {} void Circle::Rotate(double) {}
Attributes, and macros that expand to attributes, appear at the very +beginning of the function declaration or definition, before the +return type:
+MUST_USE_RESULT bool IsOK(); ++
The basic format for a class declaration (lacking the +
The basic format for a class definition (lacking the comments, see Class Comments for a discussion of what comments are needed) is:
@@ -6210,6 +6201,6 @@ more interesting. Have fun!