Background
+Background
C++ is one of the main development languages used by many of Google's open-source projects. As every C++ @@ -22,8 +22,8 @@ this power brings with it complexity, which in turn can make code more bug-prone and harder to read and maintain.
The goal of this guide is to manage this complexity by -describing in detail the dos and don'ts of writing C++ code. -These rules exist to +describing in detail the dos and don'ts of writing C++ code +. These rules exist to keep the code base manageable while still allowing coders to use C++ language features productively.
@@ -39,13 +39,11 @@ Google conform to the requirements in this guide. - -Note that this guide is not a C++ tutorial: we assume that the reader is familiar with the language.
Goals of the Style Guide
-Why do we have this document?
There are a few core goals that we believe this guide should @@ -125,7 +123,7 @@ trickier language constructs, because any benefits of more complex implementation are multiplied widely by usage, and the cost in understanding the complexity does not need to be paid again when working with new portions of the codebase. When in doubt, waivers to rules of this type -can be sought by asking +can be sought by asking your project leads. This is specifically important for our codebase because code ownership and team membership changes over time: even if everyone that works with some piece of code @@ -157,9 +155,7 @@ same as a license to proceed. Use your judgment, and if you are unsure, please don't hesitate to ask your project leads to get additional input.
-C++ Version
@@ -194,13 +190,10 @@ pitfalls of using header files.Self-contained Headers
-Header files should be self-contained (compile on their own) and
end in .h
. Non-header files that are meant for inclusion
should end in .inc
and be used sparingly.
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
@@ -229,26 +222,23 @@ their prerequisites. Name such files with the .inc
extension. Use sparingly, and prefer self-contained headers when
possible.
The #define Guard
-All header files should have #define
guards to
prevent multiple inclusion. The format of the symbol name
should be
+
<PROJECT>_<PATH>_<FILE>_H_
.
To guarantee uniqueness, they should
be based on the full path in a project's source tree. For
example, the file foo/src/bar/baz.h
in
project foo
should have the following
guard:
#ifndef FOO_BAR_BAZ_H_ #define FOO_BAR_BAZ_H_ @@ -260,24 +250,16 @@ guard: - -
Forward Declarations
-Avoid using forward declarations where possible.
- Just #include
the headers you need.
Avoid using forward declarations where possible.
+Instead, #include
the headers you need.
A "forward declaration" is a declaration of a class, function, or template without an associated definition.
-- Forward declarations can save compile time, as
#include
s force the compiler to open @@ -288,9 +270,8 @@ function, or template without an associated definition. your code to be recompiled more often, due to unrelated changes in the header.
- Forward declarations can hide a dependency, allowing user code to skip necessary recompilation when headers @@ -334,11 +315,10 @@ function, or template without an associated definition. (e.g. using pointer members instead of object members) can make the code slower and more complex. - -
- Try to avoid forward declarations of entities defined in another project. @@ -352,33 +332,24 @@ function, or template without an associated definition.
Please see Names and Order of Includes for rules about when to #include a header.
-Inline Functions
-Define functions inline only when they are small, say, 10 lines or fewer.
-You can declare functions in a way that allows the compiler to expand them inline rather than calling them through the usual function call mechanism.
-Inlining a function can generate more efficient object code, as long as the inlined function is small. Feel free to inline accessors and mutators, and other short, performance-critical functions.
-Overuse of inlining can actually make programs slower. Depending on a function's size, inlining it can cause the code size to increase or decrease. Inlining a very small @@ -386,9 +357,8 @@ accessor function will usually decrease code size while inlining a very large function can dramatically increase code size. On modern processors smaller code usually runs faster due to better use of the instruction cache.
-A decent rule of thumb is to not inline a function if it is more than 10 lines long. Beware of destructors, which are often longer than they appear because of @@ -407,23 +377,18 @@ main reason for making a virtual function inline is to place its definition in the class, either for convenience or to document its behavior, e.g., for accessors and mutators.
-Names and Order of Includes
-Use standard order for readability and to avoid hidden
-dependencies: Related header, C library, C++ library, other libraries'
-.h
, your project's .h
.
Include headers in the following order: Related header, C system headers, +C++ standard library headers, +other libraries' headers, your project's +headers.
-
All of a project's header files should be
listed as descendants of the project's source
-directory without use of UNIX directory shortcuts
+directory without use of UNIX directory aliases
.
(the current directory) or ..
(the parent directory). For example,
@@ -444,23 +409,29 @@ as follows:
.h
extension), e.g. <unistd.h>
,
+ <stdlib.h>
..h
- files.<algorithm>
, <cstddef>
..h
files..h
files.Note that any adjacent blank lines should be collapsed.
+Separate each non-empty group with one blank line.
-With the preferred ordering, if +
With the preferred ordering, if the related header
dir2/foo2.h
omits any necessary
includes, the build of dir/foo.cc
or dir/foo_test.cc
will break.
@@ -476,9 +447,9 @@ directories too.
Note that the C compatibility headers such as stddef.h
+
Note that the C headers such as stddef.h
are essentially interchangeable with their C++ counterparts
-(cstddef
)
+(cstddef
).
Either style is acceptable, but prefer consistency with existing code.
Within each section the includes should be ordered
@@ -491,21 +462,19 @@ declaration. If you rely on symbols from bar.h
,
don't count on the fact that you included foo.h
which
(currently) includes bar.h
: include bar.h
yourself, unless foo.h
explicitly demonstrates its intent
-to provide you the symbols of bar.h
. However, any
-includes present in the related header do not need to be included
-again in the related cc
(i.e., foo.cc
can
-rely on foo.h
's includes).
bar.h
.
For example, the includes in
google-awesome-project/src/foo/internal/fooserver.cc
might look like this:
#include "foo/server/fooserver.h" #include <sys/types.h> #include <unistd.h> + +#include <string> #include <vector> #include "base/basictypes.h" @@ -513,7 +482,9 @@ might look like this: #include "foo/server/bar.h"-
Sometimes, system-specific code needs +
Exception:
+ +Sometimes, system-specific code needs conditional includes. Such code can put conditional includes after other includes. Of course, keep your system-specific code small and localized. Example:
@@ -527,13 +498,10 @@ system-specific code small and localized. Example: #endif // LANG_CXX11 -Scoping
Namespaces
-With few exceptions, place code in a namespace. Namespaces should have unique names based on the project name, and possibly its path. Do not use using-directives (e.g. @@ -541,17 +509,13 @@ its path. Do not use using-directives (e.g. inline namespaces. For unnamed namespaces, see Unnamed Namespaces and Static Variables. -
Namespaces subdivide the global scope into distinct, named scopes, and so are useful for preventing name collisions in the global scope.
-Namespaces provide a method for preventing name conflicts
in large programs while allowing most code to use reasonably
@@ -569,7 +533,7 @@ can continue to refer to Foo
without the prefix.
namespace outer { +namespace outer { inline namespace inner { void foo(); } // namespace inner @@ -580,9 +544,8 @@ inline namespace inner {outer::foo()
are interchangeable. Inline namespaces are primarily intended for ABI compatibility across versions. -
Namespaces can be confusing, because they complicate the mechanics of figuring out what definition a name refers @@ -596,9 +559,8 @@ some larger versioning policy.
In some contexts, it's necessary to repeatedly refer to symbols by their fully-qualified names. For deeply-nested namespaces, this can add a lot of clutter.
-Namespaces should be used as follows:
@@ -608,7 +570,7 @@ namespaces, this can add a lot of clutter.Namespaces wrap the entire source file after - includes, + includes, gflags definitions/declarations and forward declarations of classes from other namespaces.
@@ -643,11 +605,11 @@ void MyClass::Foo() {#include "a.h" -DEFINE_FLAG(bool, someflag, false, "dummy flag"); +ABSL_FLAG(bool, someflag, false, "dummy flag"); namespace mynamespace { -using ::foo::bar; +using ::foo::Bar; ...code for mynamespace... // Code goes against the left margin. @@ -659,7 +621,8 @@ using ::foo::bar; message code in a namespace, use thepackage
specifier in the.proto
file. See - + + Protocol Buffer Packages for details.
Unnamed Namespaces and Static Variables
-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.
-
All declarations can be given internal linkage by placing them in unnamed
namespaces. Functions and variables can also be given internal linkage by
declaring them static
. This means that anything you're declaring
can't be accessed from another file. If a different file declares something with
the same name, then the two entities are completely independent.
Use of internal linkage in .cc
files is encouraged
for all code that does not need to be referenced elsewhere.
@@ -741,33 +697,26 @@ Do not use internal linkage in .h
files.
Nonmember, Static Member, and Global Functions
-Prefer placing nonmember functions in a namespace; use completely global 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.
-Nonmember and static member functions can be useful in - some situations. Putting nonmember functions in a - namespace avoids polluting the global namespace.
-Nonmember and static member functions can be useful in +some situations. Putting nonmember functions in a +namespace avoids polluting the global namespace.
-Nonmember and static member functions may make more sense as members of a new class, especially if they access external resources or have significant dependencies.
-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. @@ -781,18 +730,11 @@ common prefix, and such grouping is usually unnecessary anyway.
needed in its.cc
file, use
internal linkage to limit
its scope.
-Local Variables
-Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
-C++ allows you to declare variables anywhere in a function. We encourage you to declare them in as local a @@ -846,11 +788,8 @@ for (int i = 0; i < 1000000; ++i) { } -
Static and Global Variables
-Objects with static storage duration are forbidden unless they are @@ -866,11 +805,8 @@ for details.
As a rule of thumb: a global variable satisfies these requirements if its
declaration, considered in isolation, could be constexpr
.
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 @@ -891,16 +827,14 @@ 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.
-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.
-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 @@ -911,9 +845,8 @@ 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.
-Decision on destruction
When destructors are trivial, their execution is not subject to ordering at @@ -935,11 +868,11 @@ void foo() { // allowed: constexpr guarantees trivial destructor constexpr std::array<int, 3> kArray = {{1, 2, 3}};
// bad: non-trivial destructor -const string kFoo = "foo"; +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) -const string& kBar = StrCat("a", "b", "c"); +const std::string& kBar = StrCat("a", "b", "c"); void bar() { // bad: non-trivial destructor @@ -959,7 +892,8 @@ 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)+Bar y = g(); // ? (depends on g and on Bar::Bar) +
All but the first statement expose us to indeterminate initialization ordering.
@@ -979,6 +913,7 @@ Foo a[] = { Foo(1), Foo(2), Foo(3) }; // fine static storage duration variables should be marked withconstexpr
or where possible the
+
ABSL_CONST_INIT
attribute. Any non-local static storage
@@ -1008,7 +943,6 @@ does not make an observable difference. For example:
Dynamic initialization of static local variables is allowed (and common).
-Common patterns
@@ -1024,10 +958,15 @@ does not make an observable difference. For example: 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 ofint
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.
+ (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.
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
@@ -1037,30 +976,23 @@ 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.static const auto* const impl = new T(args...);
(If the
- initialization is more complex, it can be moved into a function or lambda
- expression.)static
+ const auto& impl = *new T(args...);
).
-thread_local Variables
-thread_local
variables that aren't declared inside a function
must be initialized with a true compile-time constant,
and this must be enforced by using the
+
ABSL_CONST_INIT
attribute. 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 = ...; @@ -1083,9 +1015,8 @@ 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 @@ -1093,9 +1024,8 @@ variables. 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.
@@ -1109,9 +1039,8 @@ variables.
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
@@ -1124,25 +1053,21 @@ variables.
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). To enforce this,
- thread_local
variables at class or namespace scope must be
- annotated with
-
-
- ABSL_CONST_INIT
- (or constexpr
, but that should be rare):
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). To enforce this, thread_local
variables
+at class or namespace scope must be annotated with
+
+
+
+ABSL_CONST_INIT
+(or constexpr
, but that should be rare):
ABSL_CONST_INIT thread_local Foo foo = ...;-
thread_local
should be preferred over other mechanisms for
- defining thread-local data.
thread_local
should be preferred over other mechanisms for
+defining thread-local data.
Classes
@@ -1152,19 +1077,14 @@ don'ts you should follow when writing a class.Doing Work in Constructors
-Avoid virtual method calls in constructors, and avoid initialization that can fail if you can't signal an error.
-It is possible to perform arbitrary initialization in the body of the constructor.
-- No need to worry about whether the class has been initialized or not. @@ -1174,9 +1094,7 @@ of the constructor. or algorithms.
- If the work calls virtual functions, these calls will not get dispatched to the subclass @@ -1197,40 +1115,28 @@ of the constructor. is done in the constructor cannot easily be handed off to, for example, another thread.
Constructors should never call virtual functions. If appropriate
-for your code
-,
+for your code ,
terminating the program may be an appropriate error handling
response. Otherwise, consider a factory function
or Init()
method as described in
-
-
TotW #42
.
Avoid Init()
methods on objects with
no other states that affect which public methods may be called
(semi-constructed objects of this form are particularly hard to work
with correctly).
Implicit Conversions
-Do not define implicit conversions. Use the explicit
keyword for conversion operators and single-argument
constructors.
Implicit conversions allow an
object of one type (called the source type) to
be used where a different type (called the destination
@@ -1263,9 +1169,8 @@ void Func(Foo f);
This kind of code isn't technically an implicit conversion, but the
language treats it as one as far as explicit
is concerned.
-
- Implicit conversions can make a type more usable and
expressive by eliminating the need to explicitly name a type
@@ -1273,14 +1178,13 @@ language treats it as one as far as
explicit
is concerned. - Implicit conversions can be a simpler alternative to
overloading, such as when a single
function with a
string_view
parameter takes the - place of separate overloads forstring
and + place of separate overloads forstd::string
andconst char*
. - List initialization syntax is a concise and expressive way of initializing objects.
- Implicit conversions can hide type-mismatch bugs, where the
destination type does not match the user's expectation, or
@@ -1306,9 +1210,8 @@ language treats it as one as far as
explicit
is concerned. the destination type is implicit, particularly if the list has only a single element.
Type conversion operators, and constructors that are
callable with a single argument, must be marked
explicit
in the class definition. As an
@@ -1316,7 +1219,7 @@ exception, copy and move constructors should not be
explicit
, since they do not perform type
conversion. Implicit conversions can sometimes be necessary and
appropriate for types that are designed to transparently wrap other
-types. In that case, contact
+types. In that case, contact
your project leads to request
a waiver of this rule.
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};
).
-Copyable and Movable Types
-A class's public API should make explicit whether the class is copyable, + +
A class's public API must make clear 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 movable type is one that can be initialized and assigned from temporaries.
@@ -1349,9 +1246,9 @@ stipulation that the value of the source does not change.std::unique_ptr<int>
is an example of a movable but not
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
+the destination). int
and std::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
+operations are the same; for std::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 @@ -1361,9 +1258,8 @@ 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.
-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, @@ -1390,9 +1286,8 @@ copy elision.
Move operations allow the implicit and efficient transfer of resources out of rvalue objects. This allows a plainer coding style in some cases.
-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
),
@@ -1408,11 +1303,10 @@ resulting bugs can be confusing and difficult to diagnose.
Every class's public interface should make explicit which copy and move +
Every class's public interface must make clear 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.
class Copyable { public: - Copyable(const Copyable& rhs) = default; - Copyable& operator=(const Copyable& rhs) = default; + Copyable(const Copyable& other) = default; + Copyable& operator=(const Copyable& other) = default; // The implicit move operations are suppressed by the declarations above. }; class MoveOnly { public: - MoveOnly(MoveOnly&& rhs); - MoveOnly& operator=(MoveOnly&& rhs); + MoveOnly(MoveOnly&& other); + MoveOnly& operator=(MoveOnly&& other); // The copy operations are implicitly deleted, but you can // spell that out explicitly if you want: @@ -1458,16 +1352,19 @@ class NotCopyableOrMovable { };-
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).
+These declarations/deletions can be omitted only if they are obvious: +
-
+
- If the class has no
private
section, like a + struct or an interface-only base class, + then the copyability/movability can be determined by the + copyability/movability of any public data members. + - If a base class clearly isn't copyable or movable, derived classes + naturally won't be either. An interface-only base class that leaves these + operations implicit is not sufficient to make concrete subclasses clear. +
- Note that if you explicitly declare or delete either the constructor or + assignment operation for copy, the other copy operation is not obvious and + must be declared or deleted. Likewise for 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 @@ -1489,17 +1386,10 @@ can use to implement it.
-Structs vs. Classes
-Use a struct
only for passive objects that
carry data; everything else is a class
.
The struct
and class
keywords behave almost identically in C++. We add our own
@@ -1507,40 +1397,58 @@ semantic meanings to each keyword, so you should use the
appropriate keyword for the data-type you're
defining.
structs
should be used for passive
-objects that carry data, and may have associated
-constants, but lack any functionality other than
-access/setting the data members. The accessing/setting of
-fields is done by directly accessing the fields rather
-than through method invocations. Methods should not
-provide behavior but should only be used to set up the
-data members, e.g., constructor, destructor,
-Initialize()
, Reset()
,
-Validate()
.
structs
should be used for passive objects that carry
+data, and may have associated constants, but lack any functionality
+other than access/setting the data members. All fields must be public,
+and accessed directly rather than through getter/setter methods. The
+struct must not have invariants that imply relationships between
+different fields, since direct user access to those fields may break
+those invariants. Methods should not provide behavior but should only
+be used to set up the data members, e.g., constructor, destructor,
+Initialize()
, Reset()
.
If more functionality is required, a +
If more functionality or invariants are required, a
class
is more appropriate. If in doubt, make
it a class
.
For consistency with STL, you can use
struct
instead of class
for
-functors and traits.
Note that member variables in structs and classes have different naming rules.
-Structs vs. Pairs and Tuples
+ +Prefer to use a struct
instead of a pair or a
+tuple whenever the elements can have meaningful names.
+ While using pairs and tuples can avoid the need to define a custom type,
+ potentially saving work when writing code, a meaningful field
+ name will almost always be much clearer when reading code than
+ .first
, .second
, or std::get<X>
.
+ While C++14's introduction of std::get<Type>
to access a
+ tuple element by type rather than index (when the type is unique) can
+ sometimes partially mitigate this, a field name is usually substantially
+ clearer and more informative than a type.
+
+ Pairs and tuples may be appropriate in generic code where there are not + specific meanings for the elements of the pair or tuple. Their use may + also be required in order to interoperate with existing code or APIs. +
Inheritance
-Composition is often more appropriate than inheritance.
When using inheritance, make it public
.
When a sub-class inherits from a base class, it includes the definitions of all the data and operations that the base class @@ -1548,9 +1456,8 @@ defines. "Interface inheritance" is inheritance from a pure abstract base class (one with no state or defined methods); all other inheritance is "implementation inheritance".
-Implementation inheritance reduces code size by re-using the base class code as it specializes an existing type. Because inheritance is a compile-time declaration, you @@ -1560,9 +1467,8 @@ programmatically enforce that a class expose a particular API. Again, the compiler can detect errors, in this case, when a class does not define a necessary method of the API.
-For implementation inheritance, because the code implementing a sub-class is spread between the base and the sub-class, it can be more difficult to understand an @@ -1577,9 +1483,8 @@ inheritance can often be greater than the performance drop from ordinary to virtual dispatch), and because it risks leading to "diamond" inheritance patterns, which are prone to ambiguity, confusion, and outright bugs.
-All inheritance should be public
. If you
want to do private inheritance, you should be including
@@ -1610,21 +1515,14 @@ present, the reader has to check all ancestors of the
class in question to determine if the function or
destructor is virtual or not.
Multiple inheritance is permitted, but multiple implementation +
Multiple inheritance is permitted, but multiple implementation inheritance is strongly discouraged.
-Operator Overloading
-Overload operators judiciously. Do not create user-defined literals.
-Overload operators judiciously. Do not use user-defined literals.
-C++ permits user code to
declare
overloaded versions of the built-in operators using the
@@ -1633,9 +1531,8 @@ is a user-defined type. The operator
keyword also
permits user code to define new kinds of literals using
operator""
, and to define type-conversion functions
such as operator bool()
.
Operator overloading can make code more concise and intuitive by enabling user-defined types to behave the same as built-in types. Overloaded operators are the idiomatic names @@ -1647,9 +1544,8 @@ those names.
User-defined literals are a very concise notation for creating objects of user-defined types.
-- Providing a correct, consistent, and unsurprising set of operator overloads requires some care, and failure @@ -1691,13 +1587,23 @@ creating objects of user-defined types. in undefined behavior, which can manifest as subtle run-time bugs. -
- User-defined literals allow the creation of new +
- User-defined literals (UDLs) allow the creation of new syntactic forms that are unfamiliar even to experienced C++ - programmers. -
"Hello World"sv
as a
+ shorthand for std::string_view("Hello World")
.
+ Existing notations are clearer, though less terse.
-Define overloaded operators only if their meaning is
obvious, unsurprising, and consistent with the corresponding
built-in operators. For example, use |
as a
@@ -1737,7 +1643,8 @@ use a custom comparator rather than overloading
Do not overload &&
, ||
,
,
(comma), or unary &
. Do not overload
operator""
, i.e. do not introduce user-defined
-literals.
Type conversion operators are covered in the section on
implicit conversions.
@@ -1747,19 +1654,12 @@ The =
operator is covered in the section on
section on streams. See also the rules on
function overloading, which
apply to operator overloading as well.
Access Control
-Make classes' data members private
, unless they are
-static const
(and follow the
-naming convention for constants).
const
) if necessary.
For technical
reasons, we allow data members of a test fixture class in a .cc file to
@@ -1769,16 +1669,10 @@ be protected
when using
Google
Test).
Declaration Order
-Group similar declarations together, placing public parts earlier.
-A class definition should usually start with a
public:
section, followed by
@@ -1798,26 +1692,18 @@ performance-critical, and very short, methods may be
defined inline. See Inline
Functions for more details.
Functions
Output Parameters
-Prefer using return values rather than output parameters. -If output-only parameters are used they should appear after -input 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.
+Prefer using return values over output parameters: they +improve readability, and often provide the same or better +performance. If output-only parameters are used, +they should appear after input parameters.
Parameters are either input to the function, output from the function, or both. Input parameters are usually values or @@ -1835,15 +1721,10 @@ both input and output (often classes/structs) muddy the waters, and, as always, consistency with related functions may require you to bend the rule.
-Write Short Functions
-Prefer small and focused functions.
-We recognize that long functions are sometimes appropriate, so no hard limit is placed on functions length. If a function exceeds about 40 lines, think about @@ -1854,10 +1735,11 @@ of the program.
someone modifying it in a few months may add new behavior. This could result in bugs that are hard to find. Keeping your functions short and simple makes it -easier for other people to read and modify your code. +easier for other people to read and modify your code. +Small functions are also easier to test.You could find long and complicated functions when -working with +working with some code. Do not be intimidated by modifying existing code: if working with such a function proves to be difficult, you find that @@ -1865,43 +1747,34 @@ errors are hard to debug, or you want to use a piece of it in several different contexts, consider breaking up the function into smaller and more manageable pieces.
-Reference Arguments
-All parameters passed by lvalue reference must be labeled
const
.
In C, if a
function needs to modify a variable, the parameter must
use a pointer, eg int foo(int *pval)
. In
C++, the function can alternatively declare a reference
parameter: int foo(int &val)
.
Defining a parameter as reference avoids ugly code like
(*pval)++
. Necessary for some applications
like copy constructors. Makes it clear, unlike with
pointers, that a null pointer is not a possible
value.
References can be confusing, as they have value syntax but pointer semantics.
-Within function parameter lists all references must be
const
:
void Foo(const string &in, string *out); +void Foo(const std::string &in, std::string *out);In fact it is a very strong convention in Google code @@ -1932,58 +1805,47 @@ T* rather than
-const T&
, do so for a concrete reason; otherwise it will likely confuse readers by making them look for an explanation that doesn't exist.
Function Overloading
-Use overloaded functions (including constructors) only if a reader looking at a call site can get a good idea of what is happening without having to first figure out exactly which overload is being called.
-You may write a function that takes a const
-string&
and overload it with another that
+std::string& and overload it with another that
takes const char*
. However, in this case consider
std::string_view
instead.
class MyClass { public: - void Analyze(const string &text); + void Analyze(const std::string &text); void Analyze(const char *text, size_t textlen); };-
Overloading can make code more intuitive by allowing an identically-named function to take different arguments. It may be necessary for templatized code, and it can be convenient for Visitors.
Overloading based on const or ref qualification may make utility code more usable, more efficient, or both. - (See TotW 148 for more.)
-If a function is overloaded by the argument types alone, a reader may have to understand C++'s complex matching rules in order to tell what's going on. Also many people are confused by the semantics of inheritance if a derived class overrides only some of the variants of a function.
-You may overload a function when there are no semantic differences between variants. These overloads may vary in types, qualifiers, or argument count. However, a reader of such a call must not need to know @@ -1991,23 +1853,16 @@ which member of the overload set is chosen, only that something from the set is being called. If you can document all entries in the overload set with a single comment in the header, that is a good sign that it is a well-designed overload set.
-Default Arguments
-Default arguments are allowed on non-virtual functions when the default is guaranteed to always have the same value. Follow the same restrictions as for function overloading, and prefer overloaded functions if the readability gained with default arguments doesn't outweigh the downsides below.
-Often you have a function that uses default values, but occasionally you want to override the defaults. Default parameters allow an easy way to do this without having to @@ -2016,9 +1871,8 @@ to overloading the function, default arguments have a cleaner syntax, with less boilerplate and a clearer distinction between 'required' and 'optional' arguments.
-Defaulted arguments are another way to achieve the semantics of overloaded functions, so all the reasons not to overload functions apply.
@@ -2037,9 +1891,8 @@ of varying at each call. default arguments, since the function signature often doesn't match the call signature. Adding function overloads avoids these problems. -Default arguments are banned on virtual functions, where they don't work properly, and in cases where the specified default might not evaluate to the same value depending on @@ -2050,17 +1903,13 @@ f(int n = counter++);.)
readability of their function declarations enough to overcome the downsides above, so they are allowed. When in doubt, use overloads. -Trailing Return Type Syntax
-Use trailing return types only where using the ordinary syntax (leading return types) is impractical or much less readable.
-C++ allows two different forms of function declarations. In the older form, the return type appears before the function name. For example:
int foo(int x);
@@ -2075,10 +1924,8 @@ doubt, use overloads.
make a difference for a simple case like int
but it matters
for more complicated cases, like types declared in class scope or
types written in terms of the function parameters.
-
Trailing return types are the only way to explicitly specify the return type of a lambda expression. In some cases the compiler is able to deduce a lambda's return type, @@ -2096,9 +1943,8 @@ doubt, use overloads.
template <typename T, typename U> decltype(declval<T&>() + declval<U&>()) add(T t, U u);-
Trailing return type syntax is relatively new and it has no analogue in C++-like languages such as C and Java, so some readers may find it unfamiliar.
@@ -2106,9 +1952,8 @@ doubt, use overloads. declarations that aren't going to get changed to use the new syntax, so the realistic choices are using the old syntax only or using a mixture of the two. Using a single version is better for uniformity of style. -In most cases, continue to use the older style of function declaration where the return type goes before the function name. Use the new trailing-return-type form only in cases where it's @@ -2118,30 +1963,26 @@ doubt, use overloads.
issue in fairly complicated template code, which is discouraged in most cases. -Google-Specific Magic
+There are various tricks and utilities that we use to make C++ code more robust, and various ways we use C++ that may differ from what you see elsewhere.
+Ownership and Smart Pointers
-Prefer to have single, fixed owners for dynamically allocated objects. Prefer to transfer ownership with smart pointers.
-"Ownership" is a bookkeeping technique for managing
dynamically allocated memory (and other resources). The
owner of a dynamically allocated object is an object or
@@ -2171,9 +2012,8 @@ a dynamically allocated object. std::shared_ptr
s
can be copied; ownership of the object is shared among
all copies, and the object is deleted when the last
std::shared_ptr
is destroyed.
- It's virtually impossible to manage dynamically allocated memory without some sort of ownership @@ -2198,9 +2038,8 @@ all copies, and the object is deleted when the last
- For const objects, shared ownership can be a simple and efficient alternative to deep copying.
- Ownership must be represented and transferred via pointers (whether smart or plain). Pointer semantics @@ -2237,9 +2076,8 @@ all copies, and the object is deleted when the last
- Smart pointers are not perfect substitutes for plain pointers.
If dynamic allocation is necessary, prefer to keep ownership with the code that allocated it. If other code needs access to the object, consider passing it a copy, @@ -2264,17 +2102,10 @@ do use shared ownership, prefer to use
Never use std::auto_ptr
. Instead, use
std::unique_ptr
.
cpplint
-Use cpplint.py
to detect style errors.
cpplint.py
is a tool that reads a source file and identifies many
@@ -2286,22 +2117,21 @@ NOLINT at the end of the line or
+
Some projects have instructions on
how to run cpplint.py
from their project
tools. If the project you are contributing to does not,
you can download
cpplint.py
separately.
Other C++ Features
Rvalue References
-Use rvalue references to:
- Define move constructors and move assignment operators. @@ -2315,30 +2145,26 @@ you can download
- Support 'perfect forwarding' in generic code.
Rvalue references
are a type of reference that can only bind to temporary
objects. The syntax is similar to traditional reference
-syntax. For example, void f(string&&
+syntax. For example,
void f(std::string&&
s);
declares a function whose argument is an
-rvalue reference to a string.
When the token '&&' is applied to an unqualified template argument in a function parameter, special template argument deduction rules apply. Such a reference is called forwarding reference.
-- Defining a move constructor (a constructor taking
an rvalue reference to the class type) makes it
possible to move a value instead of copying it. If
-
v1
is astd::vector<string>
, +v1
is astd::vector<std::string>
, for example, thenauto v2(std::move(v1))
will probably just result in some simple pointer manipulation instead of copying a large amount of data. @@ -2362,49 +2188,42 @@ rules apply. Such a reference is called forwarding reference. arguments are temporary objects and/or const. This is called 'perfect forwarding'.
-
-
- Rvalue references are not yet widely - understood. Rules like automatic synthesis of move constructors and reference - collapsing (the latter refers to the special rules that apply to a T&& - parameter in a function template) are somewhat obscure. +
- Rvalue references are not yet widely understood. Rules like reference + collapsing and the special deduction rule for forwarding references + are somewhat obscure.
- Rvalue references are often misused. Using rvalue references is counter-intuitive in signatures where the argument is expected to have a valid specified state after the function call, or where no move operation is performed.
You may use rvalue references to define move constructors and move
- assignment operators (as described in Copyable and Movable Types). See the C++ Primer for more information about
- move semantics and std::move
.
You may use rvalue references to define move constructors and move
+assignment operators (as described in
+Copyable and Movable Types). See the
+C++ Primer for more information about
+move semantics and std::move
.
You may use rvalue references to define pairs of overloads, one taking - Foo&& and the other taking const Foo&. Usually the preferred - solution is just to pass by value, but an overloaded pair of functions - sometimes yields better performance and is sometimes necessary in generic code - that needs to support a wide variety of types. As always: if you're writing - more complicated code for the sake of performance, make sure you have evidence - that it actually helps.
+You may use rvalue references to define pairs of overloads, one taking
+Foo&&
and the other taking const Foo&
.
+Usually the preferred solution is just to pass by value, but an overloaded pair
+of functions sometimes yields better performance and is sometimes necessary in
+generic code that needs to support a wide variety of types. As always: if
+you're writing more complicated code for the sake of performance, make sure you
+have evidence that it actually helps.
You may use forwarding references in conjunction with std::forward
,
- to support perfect forwarding.
You may use forwarding references in conjunction with
+std::forward
,
+to support perfect forwarding.
Friends
-We allow use of friend
classes and functions,
within reason.
Friends should usually be defined in the same file so that the reader does not have to look in another file to @@ -2423,29 +2242,25 @@ other class access to it. However, most classes should interact with other classes solely through their public members.
-Exceptions
-We do not use C++ exceptions.
-- Exceptions allow higher levels of an application to decide how to handle "can't happen" failures in deeply nested functions, without the obscuring and error-prone bookkeeping of error codes. - + +
- Exceptions are used by most other modern languages. Using them in C++ would make it more consistent with Python, Java, and the C++ that others are familiar with. +
- Some third-party C++ libraries use exceptions, and turning them off internally makes it harder to @@ -2459,9 +2274,8 @@ members.
- Exceptions are really handy in testing frameworks.
- When you add a
throw
statement to an existing function, you must examine all of its @@ -2505,9 +2319,8 @@ members. to be thrown. We would need to make the style guide even longer to document these restrictions!
On their face, the benefits of using exceptions outweigh the costs, especially in new projects. However, for existing code, the introduction of exceptions has @@ -2544,18 +2357,11 @@ features added in C++11, such as
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
@@ -2565,9 +2371,7 @@ crashes via std::terminate
.
- Specifying move constructors as
noexcept
improves performance in some cases, e.g. @@ -2582,12 +2386,11 @@ throw any exceptions. that no exceptions can be thrown due to anoexcept
specifier.
-
-
+
In projects following this guide
that have exceptions disabled it is hard
to ensure that
noexcept
@@ -2598,9 +2401,8 @@ throw any exceptions. 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
@@ -2609,7 +2411,7 @@ 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
+with
your project leads.
Prefer unconditional noexcept
if exceptions are
@@ -2637,39 +2439,18 @@ simply document that your component doesn’t support hash
functions throwing and make it unconditionally
noexcept
.
Run-Time Type Information (RTTI)
-Avoid using Run Time Type Information (RTTI).
- RTTI allows a
programmer to query the C++ class of an object at run
time. This is done by use of typeid
or
dynamic_cast
.
Querying the type of an object at run-time frequently -means a design problem. Needing to know the type of an -object at runtime is often an indication that the design -of your class hierarchy is flawed.
- -Undisciplined use of RTTI makes code hard to maintain. -It can lead to type-based decision trees or switch -statements scattered throughout the code, all of which -must be examined when making further changes.
-The standard alternatives to RTTI (described below) require modification or redesign of the class hierarchy in question. Sometimes such modifications are infeasible @@ -2693,9 +2474,19 @@ bool Derived::Equal(Base* other) { ... } -
Querying the type of an object at run-time frequently +means a design problem. Needing to know the type of an +object at runtime is often an indication that the design +of your class hierarchy is flawed.
+ +Undisciplined use of RTTI makes code hard to maintain. +It can lead to type-based decision trees or switch +statements scattered throughout the code, all of which +must be examined when making further changes.
+ +RTTI has legitimate uses but is prone to abuse, so you must be careful when using it. You may use it freely in unittests, but avoid it when possible in other code. In @@ -2745,13 +2536,9 @@ find and modify all the affected code segments.
arguments against RTTI apply just as much to workarounds like class hierarchies with type tags. Moreover, workarounds disguise your true intent. -Casting
-Use C++-style casts
like static_cast<float>(double_value)
, or brace
initialization for conversion of arithmetic types like
@@ -2759,17 +2546,13 @@ initialization for conversion of arithmetic types like
cast formats like
int y = (int)x
or int y = int(x)
(but the latter
is okay when invoking a constructor of a class type).
C++ introduced a different cast system from C that distinguishes the types of cast operations.
-The problem with C casts is the ambiguity of the operation;
sometimes you are doing a conversion
(e.g., (int)3.5
) and sometimes you are doing
@@ -2777,13 +2560,11 @@ a cast (e.g., (int)"hello"
). Brace
initialization and C++ casts can often help avoid this
ambiguity. Additionally, C++ casts are more visible when searching for
them.
The C++-style cast syntax is verbose and cumbersome.
-Do not use C-style casts. Instead, use these C++-style casts when explicit type conversion is necessary.
@@ -2793,7 +2574,7 @@ explicit type conversion is necessary. will not compile if conversion can result in information loss. The syntax is also concise. - +static_cast
as the equivalent of a C-style cast
that does value conversion, when you need to
@@ -2802,51 +2583,47 @@ explicit type conversion is necessary.
subclass. In this last case, you must be sure your object is
actually an instance of the subclass.const_cast
to remove the
const
qualifier (see const).reinterpret_cast
to do unsafe
- conversions of pointer types to and from integer and
- other pointer types. Use this only if you know what you
- are doing and you understand the aliasing issues.
- reinterpret_cast
to do unsafe conversions of
+ pointer types to and from integer and other pointer
+ types. Use this
+ only if you know what you are doing and you understand the aliasing
+ issues. Also, consider the alternative
+ absl::bit_cast
.absl::bit_cast
to interpret the raw bits of a
+ value using a different type of the same size (a type pun), such as
+ interpreting the bits of a double
as
+ int64
.See the
RTTI section for guidance on the use of
dynamic_cast
.
Streams
-Use streams where appropriate, and stick to "simple"
usages. Overload <<
for streaming only for types
representing values, and write only the user-visible value, not any
implementation details.
Streams are the standard I/O abstraction in C++, as
exemplified by the standard header <iostream>
.
-They are widely used in Google code, but only for debug logging
+They are widely used in Google code, mostly for debug logging
and test diagnostics.
The <<
and >>
stream operators provide an API for formatted I/O that
is easily learned, portable, reusable, and extensible.
printf
, by contrast, doesn't even support
-string
, to say nothing of user-defined types,
+std::string
, to say nothing of user-defined types,
and is very difficult to use portably.
printf
also obliges you to choose among the
numerous slightly different versions of that function,
@@ -2857,9 +2634,8 @@ via std::cin
, std::cout
,
std::cerr
, and std::clog
.
The C APIs do as well, but are hampered by the need to
manually buffer the input.
- Stream formatting can be configured by mutating the state of the stream. Such mutations are persistent, so @@ -2893,9 +2669,8 @@ extremely costly for the compiler. When used pervasively in a large code base, it can consume as much as 20% of the parsing and semantic analysis time.
Use streams only when they are the best tool for the job.
This is typically the case when the I/O is ad-hoc, local,
human-readable, and targeted at other developers rather than
@@ -2935,30 +2710,22 @@ details in the output of <<
; if you need to print
object internals for debugging, use named functions instead
(a method named DebugString()
is the most common
convention).
Preincrement and Predecrement
-Use prefix form (++i
) of the increment and
decrement operators with iterators and other template
objects.
When a variable
is incremented (++i
or i++
) or
decremented (--i
or i--
) and
the value of the expression is not used, one must decide
whether to preincrement (decrement) or postincrement
(decrement).
When the return value is ignored, the "pre" form
(++i
) is never less efficient than the
"post" form (i++
), and is often more
@@ -2969,36 +2736,27 @@ iterator or other non-scalar type, copying i
could be expensive. Since the two types of increment
behave the same when the value is ignored, why not just
always pre-increment?
The tradition developed, in C, of using post-increment
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 simple scalar (non-object) values there is no reason to prefer one form and we allow either. For iterators and other template types, use pre-increment.
-Use of const
-Use const
whenever it makes sense. With C++11,
+
In APIs, use const
whenever it makes sense. With C++11,
constexpr
is a better choice for some uses of
const.
Declared variables and parameters can be preceded
by the keyword const
to indicate the variables
are not changed (e.g., const int foo
). Class
@@ -3006,9 +2764,8 @@ functions can have the const
qualifier to
indicate the function does not change the state of the
class member variables (e.g., class Foo { int
Bar(char c) const; };
).
Easier for people to understand how variables are being used. Allows the compiler to do better type checking, and, conceivably, generate better code. Helps people @@ -3017,23 +2774,24 @@ know the functions they call are limited in how they can modify your variables. Helps people know what functions are safe to use without locks in multi-threaded programs.
-const
is viral: if you pass a
const
variable to a function, that function
must have const
in its prototype (or the
variable will need a const_cast
). This can
be a particular problem when calling library
functions.
const
variables, data members, methods
-and arguments add a level of compile-time type checking;
-it is better to detect errors as soon as possible.
-Therefore we strongly recommend that you use
-const
whenever it makes sense to do so:
We strongly recommend using const
+in APIs (i.e. on function parameters, methods, and
+non-local variables) wherever it is meaningful and accurate. This
+provides consistent, mostly compiler-verified documentation
+of what objects an operation can mutate. Having
+a consistent and reliable way to distinguish reads from writes
+is critical to writing thread-safe code, and is useful in
+many other contexts as well. In particular:
- If a function guarantees that it will not modify an argument
@@ -3041,25 +2799,28 @@ Therefore we strongly recommend that you use
should be a reference-to-const (
const T&
) or pointer-to-const (const T*
), respectively.
- - Declare methods to be
const
whenever - possible. Accessors should almost always be -const
. Other methods should be const if - they do not modify any data members, do not call any - non-const
methods, and do not return a - non-const
pointer or - non-const
reference to a data member.
+ - For a function parameter passed by value,
const
has + no effect on the caller, thus is not recommended in function + declarations. See - - Consider making data members
const
- whenever they do not need to be modified after - construction.
+
+ TotW #109.
+
+
+ - Declare methods to be
const
unless they + alter the logical state of the object (or enable the user to modify + that state, e.g. by returning a non-const reference, but that's + rare), or they can't safely be invoked concurrently.
The mutable
keyword is allowed but is
-unsafe when used with threads, so thread safety should be
-carefully considered first.
Using const
on local variables is neither encouraged
+nor discouraged.
All of a class's const
operations should be safe
+to invoke concurrently with each other. If that's not feasible, the class must
+be clearly documented as "thread-unsafe".
Where to put the const
Some people favor the form int const *foo
@@ -3079,45 +2840,35 @@ putting the "adjective" (const
) before the
That said, while we encourage putting
const
first, we do not require it. But be
consistent with the code around you!
Use of constexpr
-In C++11, use constexpr
to define true
constants or to ensure constant initialization.
Some variables can be declared constexpr
to indicate the variables are true constants, i.e. fixed at
compilation/link time. Some functions and constructors
can be declared constexpr
which enables them
to be used in defining a constexpr
variable.
Use of constexpr
enables definition of
constants with floating-point expressions rather than
just literals; definition of constants of user-defined
types; and definition of constants with function
calls.
Prematurely marking something as constexpr may cause migration problems if later on it has to be downgraded. Current restrictions on what is allowed in constexpr functions and constructors may invite obscure workarounds in these definitions.
-constexpr
definitions enable a more
robust specification of the constant parts of an
interface. Use constexpr
to specify true
@@ -3125,17 +2876,13 @@ constants and the functions that support their
definitions. Avoid complexifying function definitions to
enable their use with constexpr
. Do not use
constexpr
to force inlining.
Integer Types
-Of the built-in C++ integer types, the only one used
is
int
. If a program needs a variable of a
-different size, use
+different size, use
a precise-width integer type from
<stdint.h>
, such as
int16_t
. If your variable represents a
@@ -3146,31 +2893,25 @@ Keep in mind that even if your value won't ever be too large
for an int
, it may be used in intermediate
calculations which may require a larger type. When in doubt,
choose a larger type.
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.
Uniformity of declaration.
-The sizes of integral types in C++ can vary based on compiler and architecture.
-
-<stdint.h>
defines types
+<cstdint>
defines types
like int16_t
, uint32_t
,
int64_t
, etc. You should always use
those in preference to short
, unsigned
@@ -3198,11 +2939,13 @@ or
You should not use the unsigned integer types such as
+
uint32_t
, unless there is a valid
reason such as representing a bit pattern rather than a
number, or you need defined overflow modulo 2^N. In
particular, do not use unsigned types to say a number
-will never be negative. Instead, use
+will never be negative. Instead, use
+
assertions for this.
Use care when converting integer types. Integer conversions and promotions can cause undefined behavior, leading to security bugs and other problems.
-On Unsigned Integers
@@ -3238,18 +2978,11 @@ 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. -64-bit Portability
-Code should be 64-bit and 32-bit friendly. Bear in mind problems of printing, comparisons, and structure alignment.
--
@@ -3260,20 +2993,24 @@ problems of printing, comparisons, and structure alignment.
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 - + +StrCat
+ or - + +Substitute
+ for fast simple conversions, - - orstd::ostream
. + + orstd::ostream
.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 toprintf
-based APIs. Note that it is acceptable to use typedefs for which printf has dedicated length modifiers, such as @@ -3288,13 +3025,13 @@ problems of printing, comparisons, and structure alignment. - You may need to be careful with structure
alignments, particularly for structures being stored on
- disk. Any class/structure with a
+ disk. Any class/structure with a
int64_t
/uint64_t
member will by default end up being 8-byte aligned on a 64-bit system. If you have such structures being shared on disk between 32-bit and 64-bit code, you will need to ensure that they are packed the same on both - architectures. + architectures. Most compilers offer a way to alter structure alignment. For gcc, you can use__attribute__((packed))
. MSVC offers @@ -3306,24 +3043,20 @@ problems of printing, comparisons, and structure alignment. 64-bit constants. For example: +int64_t my_value{0x123456789}; uint64_t my_mask{3ULL << 48};
+
Preprocessor Macros
-Avoid defining macros, especially in headers; prefer
inline functions, enums, and const
variables.
Name macros with a project-specific prefix. Do not use
macros to define pieces of a C++ API.
Macros mean that the code you see is not the same as the code the compiler sees. This can introduce unexpected @@ -3368,7 +3101,7 @@ lower-level libraries. And some of their special features available through the language proper. But before using a macro, consider carefully whether there's a non-macro way to achieve the same result. If you need to use a macro to -define an interface, contact +define an interface, contact your project leads to request a waiver of this rule.
@@ -3401,18 +3134,10 @@ header, it must have a globally unique name. To achieve this, it must be named with a prefix consisting of your project's namespace name (but upper case). -0 and nullptr/NULL
-Use 0
for integers, 0.0
for reals,
-nullptr
for pointers, and '\0'
for chars.
Use 0
for integers and 0.0
for reals.
Use nullptr
for pointers, and '\0'
for chars (and
+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.
NULL
for
+numeric (integer or floating-point) values.
Use '\0'
for the null character. Using the correct type makes
the code more readable.
sizeof
-Prefer sizeof(varname)
to
sizeof(type)
.
Use sizeof(varname)
when you
take the size of a particular variable.
@@ -3459,20 +3179,14 @@ memset(&data, 0, sizeof(data));
}
-
auto
-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.
- C++ type names can be long and cumbersome, especially when they @@ -3483,9 +3197,8 @@ small code region, the repetition may not be aiding readability. the initialization expression, since that avoids the possibility of unintended copies or type conversions.
Sometimes code is clearer when types are manifest, especially when a variable's initialization depends on things that were declared far away. In expressions @@ -3508,10 +3221,8 @@ interface, e.g. as a constant in a header, then a programmer might change its type while only intending to change its value, leading to a more radical API change than intended.
-auto
is permitted when it increases readability,
particularly as described below. Never initialize an auto
-typed
variable with a braced initializer list.
new
and
+to new
and
std::make_unique
commonly falls into this category, as does use of auto
in
a range-based loop over a container whose type is spelled out
@@ -3539,7 +3250,7 @@ is std::pair<KeyType, ValueType>
whereas it is actually
particularly well paired with local key
and value
aliases for .first
and .second
(often const-ref).
-for (const auto& item : some_map) { +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, @@ -3550,115 +3261,14 @@ and.second
(often const-ref).
Braced Initializer List
- -You may use braced initializer lists.
-In C++03, aggregate types (arrays and structs with no -constructor) could be initialized with braced initializer lists.
- -struct Point { int x; int y; }; -Point p = {1, 2}; -- -
In C++11, this syntax was generalized, and any object type can now -be created with a braced initializer list, known as a -braced-init-list in the C++ grammar. Here are a few examples -of its use.
- -// Vector takes a braced-init-list of elements. -std::vector<string> v{"foo", "bar"}; - -// Basically the same, ignoring some small technicalities. -// You may choose to use either form. -std::vector<string> v = {"foo", "bar"}; - -// Usable with 'new' expressions. -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"}}; - -// A braced-init-list can be implicitly converted to a return type. -std::vector<int> test_function() { return {1, 2, 3}; } - -// Iterate over a braced-init-list. -for (int i : {-1, -2, -3}) {} - -// Call a function using a braced-init-list. -void TestFunction2(std::vector<int> v) {} -TestFunction2({1, 2, 3}); -- -
A user-defined type can also define a constructor and/or assignment operator
-that take std::initializer_list<T>
, which is automatically
-created from braced-init-list:
class MyType { - public: - // std::initializer_list references the underlying init list. - // It should be passed by value. - MyType(std::initializer_list<int> init_list) { - for (int i : init_list) append(i); - } - MyType& operator=(std::initializer_list<int> init_list) { - clear(); - for (int i : init_list) append(i); - } -}; -MyType m{2, 3, 5, 7}; -- -
Finally, brace initialization can also call ordinary
-constructors of data types, even if they do not have
-std::initializer_list<T>
constructors.
double d{1.23}; -// Calls ordinary constructor as long as MyOtherType has no -// std::initializer_list constructor. -class MyOtherType { - public: - explicit MyOtherType(string); - MyOtherType(int, string); -}; -MyOtherType m = {1, "b"}; -// If the constructor is explicit, you can't use the "= {}" form. -MyOtherType m{"b"}; -- -
Never assign a braced-init-list to an auto -local variable. In the single element case, what this -means can be confusing.
- -auto d = {1.23}; // d is a std::initializer_list<double> -- -
auto d = double{1.23}; // Good -- d is a double, not a std::initializer_list. -- -
See Braced_Initializer_List_Format for formatting.
- -Lambda expressions
-Use lambda expressions where appropriate. Prefer explicit captures when the lambda will escape the current scope.
-Lambda expressions are a concise way of creating anonymous function objects. They're often useful when passing functions as arguments. For example:
@@ -3698,9 +3308,8 @@ std::sort(indices.begin(), indices.end(), [&](int a, int b) { for working with function objects, such as the polymorphic wrapperstd::function
.
-- Lambdas are much more concise than other ways of
defining function objects to be passed to STL
@@ -3717,9 +3326,8 @@ wrapper
std::function
. to write functions that take bound functions as arguments.
- Variable capture in lambdas can be a source of dangling-pointer bugs, particularly if a lambda escapes the current scope. @@ -3735,9 +3343,8 @@ wrapper
std::function
.
code harder to understand.
Template metaprogramming
-Avoid complicated template programming.
-Template metaprogramming refers to a family of techniques that exploit the fact that the C++ template instantiation mechanism is Turing complete and can be used to perform arbitrary compile-time computation in the type domain.
-Template metaprogramming allows extremely flexible interfaces that
are type safe and high performance. Facilities like
Google Test,
std::tuple
, std::function
, and
Boost.Spirit would be impossible without it.
The techniques used in template metaprogramming are often obscure to anyone but language experts. Code that uses templates in complicated ways is often unreadable, and is hard to debug or maintain.
-Template metaprogramming often leads to extremely poor compiler +
Template metaprogramming often leads to extremely poor compile time error messages: even if an interface is simple, the complicated implementation details become visible when the user does something wrong.
@@ -3827,9 +3426,8 @@ tools work with an AST that only represents the structure of the code after template expansion. It can be difficult to automatically work back to the original source construct that needs to be rewritten. -Template metaprogramming sometimes allows cleaner and easier-to-use interfaces than would be possible without it, but it's also often a temptation to be overly clever. It's best used in a small number of @@ -3860,42 +3458,31 @@ error messages are part of your user interface, and your code should be tweaked as necessary so that the error messages are understandable and actionable from a user point of view.
-Boost
-Use only approved libraries from the Boost library collection.
-The Boost library collection is a popular collection of peer-reviewed, free, open-source C++ libraries.
-Boost code is generally very high-quality, is widely portable, and fills many important gaps in the C++ standard library, such as type traits and better binders.
-Some Boost libraries encourage coding practices which can hamper readability, such as metaprogramming and other advanced template techniques, and an excessively "functional" style of programming.
-In order to maintain a high level of readability for @@ -3943,6 +3530,9 @@ Currently, the following libraries are permitted:
boost/math/special_functions
boost/math/tools
boost/multi_index
We are actively considering adding other Boost features to the list, so this list may be expanded in the future.
-The following libraries are permitted, but their use -is discouraged because they've been superseded by -standard libraries in C++11:
--
-
-
- Array from
boost/array.hpp
: use - -std::array
instead.
-
- -
- Pointer Container from
boost/ptr_container
: use containers of - -std::unique_ptr
instead.
-