@@ -717,10 +726,9 @@ Do not use internal linkage in
.h
files.
Prefer placing nonmember functions in a namespace; use completely global
-functions rarely. Prefer grouping functions with a namespace instead of
-using a class as if it were a namespace. Static methods of a class should
-generally be closely related to instances of the class or the class's static
-data.
+functions rarely. Do not use a class simply to group static functions. Static
+methods of a class should generally be closely related to instances of the
+class or the class's static data.
@@ -743,26 +751,9 @@ 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
variables, and should nearly always exist in a namespace.
-Rather than creating classes only to group static member
-functions which do not share static data, use
-
namespaces instead. For a header
-
myproject/foo_bar.h
, for example, write
-
namespace myproject {
-namespace foo_bar {
-void Function1();
-void Function2();
-} // namespace foo_bar
-} // namespace myproject
-
-
instead of
-
namespace myproject {
-class FooBar {
- public:
- static void Function1();
- static void Function2();
-};
-} // namespace myproject
-
+Do not create classes only to group static member functions;
+this is no different than just giving the function names a
+common prefix, and such grouping is usually unnecessary anyway.
If you define a nonmember function and it is only
needed in its .cc
file, use
@@ -838,85 +829,281 @@ for (int i = 0; i < 1000000; ++i) {
Static and Global Variables
-
Variables of class type with
- static storage duration are forbidden: they cause hard-to-find bugs due
- to indeterminate order of construction and destruction. However, such
- variables are allowed if they are constexpr
: they have no
- dynamic initialization or destruction.
+
Objects with
+
+static storage duration are forbidden unless they are
+trivially
+destructible. Informally this means that the destructor does not do
+anything, even taking member and base destructors into account. More formally it
+means that the type has no user-defined or virtual destructor and that all bases
+and non-static members are trivially destructible.
+Static function-local variables may use dynamic initialization.
+Use of dynamic initialization for static class member variables or variables at
+namespace scope is discouraged, but allowed in limited circumstances; see below
+for details.
+
+
As a rule of thumb: a global variable satisfies these requirements if its
+declaration, considered in isolation, could be constexpr
.
-
Objects with static storage duration, including global
-variables, static variables, static class member
-variables, and function static variables, must be Plain
-Old Data (POD): only ints, chars, floats, or pointers, or
-arrays/structs of POD.
+
+
Every object has a storage duration, which correlates with its
+lifetime. Objects with static storage duration live from the point of their
+initialization until the end of the program. Such objects appear as variables at
+namespace scope ("global variables"), as static data members of classes, or as
+function-local variables that are declared with the static
+specifier. Function-local static variables are initialized when control first
+passes through their declaration; all other objects with static storage duration
+are initialized as part of program start-up. All objects with static storage
+duration are destroyed at program exit (which happens before unjoined threads
+are terminated).
-
The order in which class constructors and initializers
-for static variables are called is only partially
-specified in C++ and can even change from build to build,
-which can cause bugs that are difficult to find.
-Therefore in addition to banning globals of class type,
-we do not allow non-local static variables to be initialized
-with the result of a function, unless that function (such
-as getenv(), or getpid()) does not itself depend on any
-other globals. However, a static POD variable within
-function scope may be initialized with the result of a
-function, since its initialization order is well-defined
-and does not occur until control passes through its
-declaration.
+
Initialization may be dynamic, which means that something
+non-trivial happens during initialization. (For example, consider a constructor
+that allocates memory, or a variable that is initialized with the current
+process ID.) The other kind of initialization is static
+initialization. The two aren't quite opposites, though: static
+initialization always happens to objects with static storage duration
+(initializing the object either to a given constant or to a representation
+consisting of all bytes set to zero), whereas dynamic initialization happens
+after that, if required.
+
-
Likewise, global and static variables are destroyed
-when the program terminates, regardless of whether the
-termination is by returning from main()
or
-by calling exit()
. The order in which
-destructors are called is defined to be the reverse of
-the order in which the constructors were called. Since
-constructor order is indeterminate, so is destructor
-order. For example, at program-end time a static variable
-might have been destroyed, but code still running
-— perhaps in another thread
-— tries to access it and fails. Or the
-destructor for a static string
variable
-might be run prior to the destructor for another variable
-that contains a reference to that string.
+
+
Global and static variables are very useful for a large number of
+applications: named constants, auxiliary data structures internal to some
+translation unit, command-line flags, logging, registration mechanisms,
+background infrastructure, etc.
+
-
One way to alleviate the destructor problem is to
-terminate the program by calling
-quick_exit()
instead of exit()
.
-The difference is that quick_exit()
does not
-invoke destructors and does not invoke any handlers that
-were registered by calling atexit()
. If you
-have a handler that needs to run when a program
-terminates via quick_exit()
(flushing logs,
-for example), you can register it using
-at_quick_exit()
. (If you have a handler that
-needs to run at both exit()
and
-quick_exit()
, you need to register it in
-both places.)
+
+
Global and static variables that use dynamic initialization or have
+non-trivial destructors create complexity that can easily lead to hard-to-find
+bugs. Dynamic initialization is not ordered across translation units, and
+neither is destruction (except that destruction
+happens in reverse order of initialization). When one initialization refers to
+another variable with static storage duration, it is possible that this causes
+an object to be accessed before its lifetime has begun (or after its lifetime
+has ended). Moreover, when a program starts threads that are not joined at exit,
+those threads may attempt to access objects after their lifetime has ended if
+their destructor has already run.
+
-
As a result we only allow static variables to contain
-POD data. This rule completely disallows
-std::vector
(use C arrays instead), or
-string
(use const char []
).
-
-
-
-
If you need a static or global
-variable of a class type, consider initializing a pointer
-(which will never be freed), from either your main()
-function or from pthread_once(). Note that this must be a
-raw pointer, not a "smart" pointer, since the smart
-pointer's destructor will have the order-of-destructor
-issue that we are trying to avoid.
+
+
Decision on destruction
+
+
When destructors are trivial, their execution is not subject to ordering at
+all (they are effectively not "run"); otherwise we are exposed to the risk of
+accessing objects after the end of their lifetime. Therefore, we only allow
+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
+
+struct X { int n; };
+const X kX[] = {{1}, {2}, {3}}; // allowed
+
+void foo() {
+ static const char* const kMessages[] = {"hello", "world"}; // allowed
+}
+
+// allowed: constexpr guarantees trivial destructor
+constexpr std::array<int, 3> kArray = {{1, 2, 3}};
+
// bad: non-trivial destructor
+const string kFoo = "foo";
+
+// bad for the same reason, even though kBar is a reference (the
+// rule also applies to lifetime-extended temporary objects)
+const string& kBar = StrCat("a", "b", "c");
+
+void bar() {
+ // bad: non-trivial destructor
+ static std::map<int, int> kData = {{1, 0}, {2, 0}, {3, 0}};
+}
+
+
Note that references are not objects, and thus they are not subject to the
+constraints on destructibility. The constraint on dynamic initialization still
+applies, though. In particular, a function-local static reference of the form
+static T& t = *new T;
is allowed.
+
+
Decision on initialization
+
+
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)
+
+
All but the first statement expose us to indeterminate initialization
+ordering.
+
+
The concept we are looking for is called constant initialization in
+the formal language of the C++ standard. It means that the initializing
+expression is a constant expression, and if the object is initialized by a
+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
+
+
Constant initialization is always allowed. Constant initialization of
+static storage duration variables should be marked with constexpr
+where possible. Any non-local static storage
+duration variable that is not so marked should be presumed to have
+dynamic initialization, and reviewed very carefully.
+
+
By contrast, the following initializations are problematic:
+
+
time_t time(time_t*); // not constexpr!
+int f(); // not constexpr!
+struct Bar { Bar() {} };
+
+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
+
+
Dynamic initialization of static local variables is allowed (and common).
+
+
Common patterns
+
+ - Global strings: if you require a global or static string constant,
+ consider using a simple character array, or a char pointer to the first
+ element of a string literal. String literals have static storage duration
+ already and are usually sufficient.
+ - 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
+ 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). 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.
+ - 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
+ in this section. One simple solution is to use a plain pointer to a
+ dynamically allocated object and never delete it (see last item).
+ - Static variables of custom types: if you require static, constant data of
+ 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 binding the pointer to a function-local static pointer variable:
+
static const auto* const impl = new T(args...);
(If the
+ initialization is more complex, it can be moved into a function or lambda
+ expression.)
+
+
thread_local Variables
+
+
+
thread_local
variables that aren't declared inside a function
+must be initialized with a true compile-time constant. Prefer
+thread_local
over other ways of defining thread-local data.
+
+
+
+
+
+
Starting with C++11, variables can be declared with the
+thread_local
specifier:
+
thread_local Foo foo = ...;
+
+
Such a variable is actually a collection of objects, so that when different
+threads access it, they are actually accessing different objects.
+thread_local
variables are much like
+static storage duration variables
+in many respects. For instance, they can be declared at namespace scope,
+inside functions, or as static class members, but not as ordinary class
+members.
+
+
thread_local
variable instances are initialized much like
+static variables, except that they must be initialized separately for each
+thread, rather than once at program startup. This means that
+thread_local
variables declared within a function are safe, but
+other thread_local
variables are subject to the same
+initialization-order issues as static variables (and more besides).
+
+
thread_local
variable instances are destroyed when their thread
+terminates, so they do not have the destruction-order issues of static
+variables.
+
+
+
+
+ - Thread-local data is inherently safe from races (because only one thread
+ can ordinarily access it), which makes
thread_local
useful for
+ concurrent programming.
+ thread_local
is the only standard-supported way of creating
+ thread-local data.
+
+
+
+
+
+ - Accessing a
thread_local
variable may trigger execution of
+ an unpredictable and uncontrollable amount of other code.
+ thread_local
variables are effectively global variables,
+ and have all the drawbacks of global variables other than lack of
+ thread-safety.
+ - The memory consumed by a
thread_local
variable scales with
+ the number of running threads (in the worst case), which can be quite large
+ in a program.
+ - An ordinary class member cannot be
thread_local
.
+ thread_local
may not be as efficient as certain compiler
+ intrinsics.
+
+
+
+
+
thread_local
variables inside a function have no safety
+ concerns, so they can be used without restriction. Note that you can use
+ a function-scope thread_local
to simulate a class- or
+ namespace-scope thread_local
by defining a function or
+ static method that exposes it:
+
+
Foo& MyThreadLocalFoo() {
+ thread_local Foo result = ComplicatedInitialization();
+ return result;
+}
+
+
+
thread_local
variables at class or namespace scope must be
+ initialized with a true compile-time constant (i.e. they must have no
+ dynamic initialization).
+
+
+
+
thread_local
should be preferred over other mechanisms for
+ defining thread-local data.
+
+
+
+
+
Classes
Classes are the fundamental unit of code in C++. Naturally,
@@ -1086,7 +1273,7 @@ your project leads to request
a waiver of this rule.
Constructors that cannot be called with a single argument
-should usually omit explicit
. Constructors that
+may omit explicit
. Constructors that
take a single std::initializer_list
parameter should
also omit explicit
, in order to support copy-initialization
(e.g. MyType m = {1, 2};
).
@@ -1097,25 +1284,32 @@ also omit
explicit
, in order to support copy-initialization
Copyable and Movable Types
-
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.
-
+
A class's public API should make explicit whether the class is copyable,
+move-only, or neither copyable nor movable. Support copying and/or
+moving if these operations are clear and meaningful for your type.
+
-
A copyable type allows its objects to be initialized or assigned
-from any other object of the same type, without changing the value of the source.
-For user-defined types, the copy behavior is defined by the copy
-constructor and the copy-assignment operator.
-string
is an example of a copyable type.
-
A movable type is one that can be initialized and assigned
-from temporaries (all copyable types are therefore movable).
+from temporaries.
+
+
A copyable type is one that can be initialized or assigned from
+any other object of the same type (so is also movable by definition), with the
+stipulation that the value of the source does not change.
std::unique_ptr<int>
is an example of a movable but not
-copyable type. For user-defined types, the move behavior is defined by the move
-constructor and the move-assignment operator.
+copyable type (since the value of the source
+
std::unique_ptr<int>
must be modified during assignment to
+the destination).
int
and
string
are examples of
+movable types that are also copyable. (For
int
, the move and copy
+operations are the same; for
string
, there exists a move operation
+that is less expensive than a copy.)
+
+
For user-defined types, the copy behavior is defined by the copy
+constructor and the copy-assignment operator. Move behavior is defined by the
+move constructor and the move-assignment operator, if they exist, or by the
+copy constructor and the copy-assignment operator otherwise.
The copy/move constructors can be implicitly invoked by the compiler
in some situations, e.g. when passing objects by value.
@@ -1170,49 +1364,82 @@ encourage excessive copying, which can cause performance problems.
-
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.
-
+
Every class's public interface should make explicit which copy and move
+operations the class supports. This should usually take the form of explicitly
+declaring and/or deleting the appropriate operations in the public
+section of the declaration.
-
If your type provides copy operations, it is recommended that you design
-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.
+
Specifically, a copyable class should explicitly declare the copy
+operations, a move-only class should explicitly declare the move operations,
+and a non-copyable/movable class should explicitly delete the copy operations.
+Explicitly declaring or deleting all four copy/move operations is permitted,
+but not required. If you provide a copy or move assignment operator, you
+must also provide the corresponding constructor.
-
class Foo {
+class Copyable {
public:
- Foo(Foo&& other) : field_(other.field) {}
- // Bad, defines only move constructor, but not operator=.
+ Copyable(const Copyable& rhs) = default;
+ Copyable& operator=(const Copyable& rhs) = default;
- private:
- Field field_;
+ // The implicit move operations are suppressed by the declarations above.
+};
+
+class MoveOnly {
+ public:
+ MoveOnly(MoveOnly&& rhs);
+ MoveOnly& operator=(MoveOnly&& rhs);
+
+ // The copy operations are implicitly deleted, but you can
+ // spell that out explicitly if you want:
+ MoveOnly(const MoveOnly&) = delete;
+ MoveOnly& operator=(const MoveOnly&) = delete;
+};
+
+class NotCopyableOrMovable {
+ public:
+ // Not copyable or movable
+ NotCopyableOrMovable(const NotCopyableOrMovable&) = delete;
+ NotCopyableOrMovable& operator=(const NotCopyableOrMovable&)
+ = delete;
+
+ // The move operations are implicitly disabled, but you can
+ // spell that out explicitly if you want:
+ NotCopyableOrMovable(NotCopyableOrMovable&&) = delete;
+ NotCopyableOrMovable& operator=(NotCopyableOrMovable&&)
+ = delete;
};
-Due to the risk of slicing, avoid providing an assignment
-operator or public copy/move constructor for a class that's
-intended to be derived from (and avoid deriving from a class
+
These declarations/deletions can be omitted only if they are obvious: for
+example, if a base class isn't copyable or movable, derived classes naturally
+won't be either. Similarly, a struct's
+copyability/movability is normally determined by the copyability/movability
+of its data members (this does not apply to classes because in Google code
+their data members are not public). Note that if you explicitly declare or
+delete any of the copy/move operations, the others are not obvious, and so
+this paragraph does not apply (in particular, the rules in this section
+that apply to "classes" also apply to structs that declare or delete any
+copy/move operations).
+
+A type should not be copyable/movable if the meaning of
+copying/moving is unclear to a casual user, or if it incurs unexpected
+costs. Move operations for copyable types are strictly a performance
+optimization and are a potential source of bugs and complexity, so
+avoid defining them unless they are significantly more efficient than
+the corresponding copy 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. Remember to review the correctness of any
+defaulted operations as you would any other code.
+
+Due to the risk of slicing, prefer to avoid providing a public assignment
+operator or copy/move constructor for a class that's
+intended to be derived from (and prefer to avoid deriving from a class
with such members). If your base class needs to be
copyable, provide a public virtual Clone()
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
in
-the public:
section:
-// MyClass is neither copyable nor movable.
-MyClass(const MyClass&) = delete;
-MyClass& operator=(const MyClass&) = delete;
-
-
-
@@ -1294,9 +1521,7 @@ implementing a sub-class is spread between the base and
the sub-class, it can be more difficult to understand an
implementation. The sub-class cannot override functions
that are not virtual, so the sub-class cannot change
-implementation. The base class may also define some data
-members, so that specifies physical layout of the base
-class.
+implementation.
@@ -1312,23 +1537,15 @@ subclasses
Foo
if it can reasonably be said
that
Bar
"is a kind of"
Foo
.
-
Make your destructor virtual
if
-necessary. If your class has virtual methods, its
-destructor should be virtual.
-
Limit the use of protected
to those
member functions that might need to be accessed from
subclasses. Note that data
members should be private.
-
Explicitly annotate overrides of virtual functions
-or virtual destructors with an override
-or (less frequently) final
specifier.
-Older (pre-C++11) code will use the
-virtual
keyword as an inferior
-alternative annotation. For clarity, use exactly one of
-override
, final
, or
-virtual
when declaring an override.
+
Explicitly annotate overrides of virtual functions or virtual
+destructors with exactly one of an override
or (less
+frequently) final
specifier. Do not
+use virtual
when declaring an override.
Rationale: A function or destructor marked
override
or final
that is
not an override of a base class virtual function will
@@ -1594,14 +1811,20 @@ apply to operator overloading as well.
+
+
+
+
For technical
reasons, we allow data members of a test fixture class to
be protected
when using
Google
Test).
-
+
+
Declaration Order
@@ -1634,24 +1857,33 @@ Functions for more details.
Functions
-
Parameter Ordering
+
+
Output Parameters
-
When defining a function, parameter order is: inputs, then
-outputs.
+
Prefer using return values rather than output parameters.
+If output-only parameters are used they should appear after
+input parameters.
-
Parameters to C/C++ functions are either input to the
-function, output from the function, or both. Input
-parameters are usually values or const
-references, while output and input/output parameters will
-be pointers to non-const
. When ordering
-function parameters, put all input-only parameters before
-any output parameters. In particular, do not add new
-parameters to the end of the function just because they
-are new; place new input-only parameters before the
-output parameters.
+
The output of a C++ function is naturally provided via
+a return value and sometimes via output parameters.
+
+
Prefer using return values instead of output parameters
+since they improve readability and oftentimes provide the same
+or better performance.
+
+
Parameters are either input to the function, output from the
+function, or both. Input parameters are usually values or
+const
references, while output and input/output
+parameters will be pointers to non-const
.
+
+
When ordering function parameters, put all input-only
+parameters before any output parameters. In particular,
+do not add new parameters to the end of the function just
+because they are new; place new input-only parameters before
+the output parameters.
This is not a hard-and-fast rule. Parameters that are
both input and output (often classes/structs) muddy the
@@ -1773,7 +2005,9 @@ which overload is being called.
You may write a function that takes a const
string&
and overload it with another that
-takes const char*
.
+takes
const char*
. However, in this case consider
+std::string_view
+ instead.
class MyClass {
public:
@@ -1800,13 +2034,13 @@ function.
-
If you want to overload a function, consider qualifying
-the name with some information about the arguments, e.g.,
-AppendString()
, AppendInt()
-rather than just Append()
. If you are
-overloading a function to support variable number of
-arguments of the same type, consider making it take a
-std::vector
so that the user can use an
+
You may overload a function when there are no semantic
+differences between variants, or when the differences are
+clear at the callsite.
+
+
If you are overloading a function to support variable
+number of arguments of the same type, consider making it
+take a std::vector
so that the user can use an
initializer list
to specify the arguments.
@@ -1907,9 +2141,13 @@ doubt, use overloads.
after the function's parameter list has already appeared. This is
particularly true when the return type depends on template parameters.
For example:
-
template <class T, class U> auto add(T t, U u) -> decltype(t + u);
+
template <typename T, typename U>
+ auto add(T t, U u) -> decltype(t + u);
+
versus
-
template <class T, class U> decltype(declval<T&>() + declval<U&>()) add(T t, U u);
+
template <typename T, typename U>
+ decltype(declval<T&>() + declval<U&>()) add(T t, U u);
+
@@ -2085,8 +2323,7 @@ do use shared ownership, prefer to use
cpplint
-
Use cpplint.py
-to detect style errors.
+
Use cpplint.py
to detect style errors.
@@ -2322,13 +2559,107 @@ exceptions in Google open-source projects as well.
Things would probably be different if we had to do it all
over again from scratch.
-
This prohibition also applies to the exception-related
-features added in C++11, such as noexcept
,
-std::exception_ptr
, and
+
This prohibition also applies to the exception handling related
+features added in C++11, such as
+std::exception_ptr
and
std::nested_exception
.
There is an exception to
this rule (no pun intended) for Windows code.
+
+
+
+
+
+
noexcept
+
+
+
Specify noexcept
when it is useful and correct.
+
+
+
+
+
The noexcept
specifier is used to specify whether
+a function will throw exceptions or not. If an exception
+escapes from a function marked noexcept
, the program
+crashes via std::terminate
.
+
+
The noexcept
operator performs a compile-time
+check that returns true if an expression is declared to not
+throw any exceptions.
+
+
+
+
+
+ - Specifying move constructors as
noexcept
+ improves performance in some cases, e.g.
+ std::vector<T>::resize()
moves rather than
+ copies the objects if T's move constructor is
+ noexcept
.
+
+ - Specifying
noexcept
on a function can
+ trigger compiler optimizations in environments where
+ exceptions are enabled, e.g. compiler does not have to
+ generate extra code for stack-unwinding, if it knows
+ that no exceptions can be thrown due to a
+ noexcept
specifier.
+
+
+
+
+
+ -
+
+ In projects following this guide
+ that have exceptions disabled it is hard
+ to ensure that
noexcept
+ specifiers are correct, and hard to define what
+ correctness even means.
+
+ - It's hard, if not impossible, to undo
noexcept
+ because it eliminates a guarantee that callers may be relying
+ on, in ways that are hard to detect.
+
+
+
+
+
You may use noexcept
when it is useful for
+performance if it accurately reflects the intended semantics
+of your function, i.e. that if an exception is somehow thrown
+from within the function body then it represents a fatal error.
+You can assume that noexcept
on move constructors
+has a meaningful performance benefit. If you think
+there is significant performance benefit from specifying
+noexcept
on some other function, please discuss it
+with
+your project leads.
+
+
Prefer unconditional noexcept
if exceptions are
+completely disabled (i.e. most Google C++ environments).
+Otherwise, use conditional noexcept
specifiers
+with simple conditions, in ways that evaluate false only in
+the few cases where the function could potentially throw.
+The tests might include type traits check on whether the
+involved operation might throw (e.g.
+std::is_nothrow_move_constructible
for
+move-constructing objects), or on whether allocation can throw
+(e.g. absl::default_allocator_is_nothrow
for
+standard default allocation). Note in many cases the only
+possible cause for an exception is allocation failure (we
+believe move constructors should not throw except due to
+allocation failure), and there are many applications where it’s
+appropriate to treat memory exhaustion as a fatal error rather
+than an exceptional condition that your program should attempt
+to recover from. Even for other
+potential failures you should prioritize interface simplicity
+over supporting all possible exception throwing scenarios:
+instead of writing a complicated noexcept
clause
+that depends on whether a hash function can throw, for example,
+simply document that your component doesn’t support hash
+functions throwing and make it unconditionally
+noexcept
.
+
@@ -2380,7 +2711,7 @@ objects. Consider
bool Base::Equal(Base* other) = 0;
bool Derived::Equal(Base* other) {
Derived* that = dynamic_cast<Derived*>(other);
- if (that == NULL)
+ if (that == nullptr)
return false;
...
}
@@ -2519,7 +2850,9 @@ RTTI section for guidance on the use of
Use streams where appropriate, and stick to "simple"
-usages.
+usages. Overload
<<
for streaming only for types
+representing values, and write only the user-visible value, not any
+implementation details.
@@ -2576,12 +2909,7 @@ flawed.
The streams API is subtle and complex, so programmers must
-develop experience with it in order to use it effectively.
-However, streams were historically banned in Google code (except
-for logging and diagnostics), so Google engineers tend not to
-have that experience. Consequently, streams-based code is likely
-to be less readable and maintainable by Googlers than code based
-on more familiar abstractions.
+develop experience with it in order to use it effectively.
Resolving the many overloads of <<
is
extremely costly for the compiler. When used pervasively in a
@@ -2834,8 +3162,9 @@ choose a larger type.
-
C++ does not specify the sizes of its integer types.
-Typically people assume that short
is 16 bits,
+
C++ does not specify the sizes of integer types
+like int
. Typically people assume
+that short
is 16 bits,
int
is 32 bits, long
is 32 bits
and long long
is 64 bits.
@@ -2894,35 +3223,32 @@ sure to use a type that will accommodate any possible
usage of your container. When in doubt, use a larger type
rather than a smaller type.
-
Use care when converting integer types. Integer
-conversions and promotions can cause non-intuitive
-behavior.
+
Use care when converting integer types. Integer conversions and
+promotions can cause undefined behavior, leading to security bugs and
+other problems.
On Unsigned Integers
-
Some people, including some textbook authors,
-recommend using unsigned types to represent numbers that
-are never negative. This is intended as a form of
-self-documentation. However, in C, the advantages of such
-documentation are outweighed by the real bugs it can
-introduce. Consider:
+
Unsigned integers are good for representing bitfields and modular
+arithmetic. Because of historical accident, the C++ standard also uses
+unsigned integers to represent the size of containers - many members
+of the standards body believe this to be a mistake, but it is
+effectively impossible to fix at this point. The fact that unsigned
+arithmetic doesn't model the behavior of a simple integer, but is
+instead defined by the standard to model modular arithmetic (wrapping
+around on overflow/underflow), means that a significant class of bugs
+cannot be diagnosed by the compiler. In other cases, the defined
+behavior impedes optimization.
-
for (unsigned int i = foo.Length()-1; i >= 0; --i) ...
-
-
-
This code will never terminate! Sometimes gcc will
-notice this bug and warn you, but often it will not.
-Equally bad bugs can occur when comparing signed and
-unsigned variables. Basically, C's type-promotion scheme
-causes unsigned types to behave differently than one
-might expect.
-
-
So, document that a variable is non-negative using
-assertions. Don't use an unsigned
-type.
+
That said, mixing signedness of integer types is responsible for an
+equally large class of problems. The best advice we can provide: try
+to use iterators and containers rather than pointers and sizes, try
+not to mix signedness, and try to avoid unsigned types (except for
+representing bitfields or modular arithmetic). Do not use an unsigned
+type merely to assert that a variable is non-negative.
@@ -2938,109 +3264,27 @@ problems of printing, comparisons, and structure alignment.
-
-
printf()
specifiers for some types
- are not cleanly portable between 32-bit and 64-bit
- systems. C99 defines some portable format specifiers.
- Unfortunately, MSVC 7.1 does not understand some of
- these specifiers and the standard is missing a few,
- so we
- have to define our own ugly versions in some cases
- (in the style of the standard include file
- inttypes.h
):
-
-
-
// printf macros for size_t, in the style of inttypes.h
-#ifdef _LP64
-#define __PRIS_PREFIX "z"
-#else
-#define __PRIS_PREFIX
-#endif
-
-// Use these macros after a % in a printf format string
-// to get correct 32/64 bit behavior, like this:
-// size_t size = records.size();
-// printf("%" PRIuS "\n", size);
-
-#define PRIdS __PRIS_PREFIX "d"
-#define PRIxS __PRIS_PREFIX "x"
-#define PRIuS __PRIS_PREFIX "u"
-#define PRIXS __PRIS_PREFIX "X"
-#define PRIoS __PRIS_PREFIX "o"
-
-
-
-
-
- Type |
- DO NOT use |
- DO use |
- Notes |
-
-
-
- void * (or any pointer) |
- %lx |
- %p |
- |
-
-
+ Correct portable printf()
conversion specifiers for
+ some integral typedefs rely on macro expansions that we find unpleasant to
+ use and impractical to require (the PRI
macros from
+ <cinttypes>
). Unless there is no reasonable alternative
+ for your particular case, try to avoid or even upgrade APIs that rely on the
+ printf
family. Instead use a library supporting typesafe numeric
+ formatting, such as
+ std::ostream
.
-
- int64_t |
- %qd , %lld |
- %" PRId64 " |
- |
-
-
-
-
-
- uint64_t |
- %qu , %llu ,
- %llx |
- %" PRIu64 " ,
- %" PRIx64 " |
- |
-
-
-
-
-
- size_t |
- %u |
- %" PRIuS " , %" PRIxS " |
-
- C99 specifies %zu |
-
-
-
- ptrdiff_t |
- %d |
- %" PRIdS " |
-
- C99 specifies %td |
-
-
-
-
-
- Note that the PRI*
macros expand to
- independent strings which are concatenated by the
- compiler. Hence if you are using a non-constant
- format string, you need to insert the value of the
- macro into the format, rather than the name. Note also
- that spaces are required around the macro identifier to
- separate it from the string literal. It is
- still possible, as usual, to include length
- specifiers, etc., after the %
when using
- the PRI*
macros. So, e.g.
- printf("x = %30" PRIuS "\n", x)
would
- expand on 32-bit Linux to printf("x = %30" "u"
- "\n", x)
, which the compiler will treat as
- printf("x = %30u\n", x)
.
-
+ Unfortunately, the PRI
macros are the only portable way to
+ specify a conversion for the standard bitwidth typedefs (e.g.
+ int64_t
, uint64_t
, int32_t
,
+ uint32_t
, etc).
+ Where possible, avoid passing arguments of types specified by bitwidth
+ typedefs to printf
-based APIs. Note that it is acceptable
+ to use typedefs for which printf has dedicated length modifiers, such as
+ size_t
(z
),
+ ptrdiff_t
(t
), and
+ maxint_t
(j
).
- Remember that
sizeof(void *)
!=
@@ -3063,13 +3307,12 @@ problems of printing, comparisons, and structure alignment.
__declspec(align())
.
-
-
Use the LL
or ULL
- suffixes as needed to create 64-bit constants. For
- example:
+ Use braced-initialization as needed to create
+ 64-bit constants. For example:
-int64_t my_value = 0x123456789LL;
-uint64_t my_mask = 3ULL << 48;
+int64_t my_value{0x123456789};
+uint64_t my_mask{3ULL << 48};
@@ -3168,30 +3411,25 @@ name (but upper case).
0 and nullptr/NULL
-
Use 0
for integers, 0.0
for
-reals, nullptr
(or NULL
) for
-pointers, and '\0'
for chars.
+
Use 0
for integers, 0.0
for reals,
+nullptr
for pointers, and '\0'
for chars.
-
Use 0
for integers and 0.0
-for reals. This is not controversial.
+
Use 0
for integers and 0.0
for reals.
-
For
-pointers (address values), there is a choice between
-0
, NULL
, and
-nullptr
. For projects that allow C++11
-features, use nullptr
. For C++03 projects,
-we prefer NULL
because it looks like a
-pointer. In fact, some C++ compilers provide special
-definitions of NULL
which enable them to
-give useful warnings, particularly in situations where
-sizeof(NULL)
is not equal to
-sizeof(0)
.
+
For pointers (address values), there is a choice between 0
,
+NULL
, and nullptr
. For projects that allow C++11
+features, use nullptr
, as this provides type-safety.
-
Use '\0'
for chars. This is the correct
-type and also makes code more readable.
+
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.
+
+
Use '\0'
for the null character. Using the correct type makes
+the code more readable.
@@ -3241,8 +3479,8 @@ readability.
-
-
+
+
- C++ type names can be long and cumbersome, especially when they
involve templates or namespaces.
- When a C++ type name is repeated within a single declaration or a
@@ -3349,7 +3587,7 @@ std::vector<string> v{"foo", "bar"};
std::vector<string> v = {"foo", "bar"};
// Usable with 'new' expressions.
-auto p = new vector<string>{"foo", "bar"};
+auto p = new std::vector<string>{"foo", "bar"};
// A map can take a list of pairs. Nested braced-init-lists work.
std::map<int, string> m = {{1, "one"}, {2, "2"}};
@@ -3938,7 +4176,8 @@ guide, the following C++11 features may not be used:
Foo f = {.field = 3}
), inline assembly, __COUNTER__
,
__PRETTY_FUNCTION__
, compound statement expressions (e.g.
foo = ({ int x; Bar(&x); x })
, variable-length arrays and
- alloca()
, and the a?:b
syntax.
+ alloca()
, and the "Elvis Operator"
+ a?:b
.
@@ -3986,6 +4225,10 @@ using Bar = Foo;
using other_namespace::Foo;
+
In new code, using
is preferable to typedef
,
+ because it provides a more consistent syntax with the rest of C++ and works
+ with templates.
+
Like other declarations, aliases declared in a header file are part of that
header's public API unless they're in a function definition, in the private portion of a class,
or in an explicitly-marked internal namespace. Aliases in such areas or in .cc files are
@@ -4031,32 +4274,32 @@ implementation retain some degree of freedom to change the alias.
For example, these aliases document how they are intended to be used in client code:
-
namespace a {
+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::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>;
-} // namespace a
+} // namespace mynamespace
These aliases don't document intended use, and half of them aren't meant for client use:
-namespace a {
+namespace mynamespace {
// Bad: none of these say how they should be used.
-using DataPoint = foo::bar::Bar*;
+using DataPoint = foo::Bar*;
using std::unordered_set; // Bad: just for local convenience
using std::hash; // Bad: just for local convenience
typedef unordered_set<DataPoint, hash<DataPoint>, DataPointComparator> TimeSeries;
-} // namespace a
+} // namespace mynamespace
However, local convenience aliases are fine in function definitions, private sections of
classes, explicitly marked internal namespaces, and in .cc files:
// In a .cc file
-using std::unordered_set;
+using foo::Bar;
@@ -4091,11 +4334,17 @@ more important to make your code immediately
understandable by a new reader. Do not use abbreviations
that are ambiguous or unfamiliar to readers outside your
project, and do not abbreviate by deleting letters within
-a word.
+a word. Abbreviations that would be familiar to someone
+outside your project with relevant domain knowledge are OK.
+As a rule of thumb, an abbreviation is probably OK if it's listed
+in
+
+Wikipedia.
int price_count_reader; // No abbreviation.
int num_errors; // "num" is a widespread convention.
int num_dns_connections; // Most people know what "DNS" stands for.
+int lstm_size; // "LSTM" is a common machine learning abbreviation.
int n; // Meaningless.
@@ -4104,12 +4353,20 @@ int n_comp_conns; // Ambiguous abbreviation.
int wgc_connections; // Only your group knows what this stands for.
int pc_reader; // Lots of things can be abbreviated "pc".
int cstmr_id; // Deletes internal letters.
+FooBarRequestInfo fbri; // Not even a word.
Note that certain universally-known abbreviations are OK, such as
i
for an iteration variable and T
for a
template parameter.
+
For some symbols, this style guide recommends names to start with a capital
+letter and to have a capital letter for each new word (a.k.a.
+"Camel Case"
+or "Pascal case"). When abbreviations appear in such names, prefer to
+capitalize the abbreviations as single words (i.e. StartRpc()
,
+not StartRPC()
).
+
Template parameters should follow the naming style for their
category: type template parameters should follow the rules for
type names, and non-type template
@@ -4268,9 +4525,9 @@ versus a class.
see
Storage Duration for details) should be named this way. This
convention is optional for variables of other storage classes, e.g. automatic
- variables, otherwise the usual variable naming rules apply.
+ variables, otherwise the usual variable naming rules apply.
-
+
Ordinarily, functions should start with a capital letter and have a
-capital letter for each new word
-(a.k.a. "Camel
-Case" or "Pascal case"). Such names should not have
-underscores. Prefer to capitalize acronyms as single words
-(i.e. StartRpc()
, not StartRPC()
).
+capital letter for each new word.
AddTableEntry()
DeleteUrl()
@@ -4295,8 +4548,8 @@ OpenFileOrDie()
(The same naming rule applies to class- and namespace-scope
constants that are exposed as part of an API and that are intended to look
-like functions, because the fact that they're
-objects rather than functions is an unimportant implementation detail.)
+like functions, because the fact that they're objects rather than functions
+is an unimportant implementation detail.)
Accessors and mutators (get and set functions) may be named like
variables. These often correspond to actual member variables, but this is
@@ -4318,7 +4571,7 @@ between nested namespaces and well-known top-level namespaces.
The name of a top-level namespace should usually be the
name of the project or team whose code is contained in that
namespace. The code in that namespace should usually be in
-a directory whose basename matches the namespace name (or
+a directory whose basename matches the namespace name (or in
subdirectories thereof).
@@ -4398,7 +4651,8 @@ names are actually causing a compile-time problem.
You're not really going to
define a macro, are you? If you do, they're like this:
-MY_MACRO_THAT_SCARES_SMALL_CHILDREN
.
+
MY_MACRO_THAT_SCARES_SMALL_CHILDREN_AND_ADULTS_ALIKE
.
+
@@ -4500,7 +4754,9 @@ license used by the project (for example, Apache 2.0,
BSD, LGPL, GPL).
If you make significant changes to a file with an
-author line, consider deleting the author line.
+author line, consider deleting the author line.
+New files should usually not contain copyright notice or
+author line.
File Contents
@@ -4619,13 +4875,7 @@ Iterator* GetIterator() const;
However, do not be unnecessarily verbose or state the
-completely obvious. Notice below that it is not necessary
- to say "returns false otherwise" because this is
-implied.
-
-
// Returns true if the table cannot hold any more entries.
-bool IsTableFull();
-
+completely obvious.
When documenting function overrides, focus on the
specifics of the override itself, rather than repeating
@@ -4791,7 +5041,7 @@ one of the following remedies:
Replace large or complex nested expressions with named variables.
As a last resort, use comments to clarify argument meanings at the
- call site.
+ call site.
Consider the following example:
@@ -5135,8 +5385,8 @@ not fit on a single line as you would wrap arguments in a
- Choose good parameter names.
- - Parameter names may be omitted only if the parameter is unused and its
- purpose is obvious.
+ - A parameter name may be omitted only if the parameter is not used in the
+ function's definition.
- If you cannot fit the return type and the function
name on a single line, break between them.
@@ -5429,7 +5679,7 @@ them; conditional or loop statements with complex
conditions or statements may be more readable with curly
braces. Some
projects require that an
-if
must always always have an accompanying
+if
must always have an accompanying
brace.
if (condition)
@@ -5475,7 +5725,7 @@ if (condition) {
Switch statements may use braces for blocks. Annotate
non-trivial fall-through between cases.
Braces are optional for single-statement loops.
-Empty loop bodies should use empty braces or continue
.
+Empty loop bodies should use either empty braces or continue
.
@@ -5489,10 +5739,9 @@ should be placed as shown below.
statements should always have a
default
case
(in the case of an enumerated value, the compiler will
warn you if any values are not handled). If the default
-case should never execute, simply
-
assert
:
+case should never execute, treat this as an error. For example:
-
+
switch (var) {
@@ -5526,8 +5775,8 @@ for (int i = 0; i < kSomeNumber; ++i) {
-
Empty loop bodies should use an empty pair of braces or continue
,
-but not a single semicolon.
+
Empty loop bodies should use either an empty pair of braces or
+continue
with no braces, rather than a single semicolon.
while (condition) {
// Repeat test until it returns false.
@@ -5582,6 +5831,11 @@ char* c;
const string& str;
+
You should do this consistently within a single
+file,
+so, when modifying an existing file, use the style in
+that file.
+
It is allowed (if unusual) to declare multiple variables in the same
declaration, but it is disallowed if any of those have pointer or
reference decorations. Such declarations are easily misread.
@@ -5593,11 +5847,6 @@ char * c; // Bad - spaces on both sides of *
const string & str; // Bad - spaces on both sides of &
-
You should do this consistently within a single
-file,
-so, when modifying an existing file, use the style in
-that file.
-
Boolean Expressions
@@ -5691,8 +5940,8 @@ will call a default constructor if available. To force the
non-
std::initializer_list
constructor, use parentheses
instead of braces.
-
std::vector<int> v(100, 1); // A vector of 100 1s.
-std::vector<int> v{100, 1}; // A vector of 100, 1.
+std::vector<int> v(100, 1); // A vector containing 100 items: All 1s.
+std::vector<int> v{100, 1}; // A vector containing 2 items: 100 and 1.
Also, the brace form prevents narrowing of integral
@@ -5867,7 +6116,7 @@ void foo() { // Correct. No extra indentation within namespace.
namespace {
- // Wrong. Indented when it should not be.
+ // Wrong! Indented when it should not be.
void foo() {
...
}
@@ -6138,7 +6387,7 @@ occasionally need to break on Windows:
-Parting Words
+Parting Words
Use common sense and BE CONSISTENT.