From 3591b2e540cbcb07423e02d20eee482165776603 Mon Sep 17 00:00:00 2001
From: Geoff Romer
One way in which we keep the code base manageable is by -enforcing consistency. It is very -important that any -programmer be able to look at -another's code and quickly understand it. Maintaining a uniform -style and following conventions means that we can more easily -use "pattern-matching" to infer what various symbols are and -what invariants are true about them. Creating common, required -idioms and patterns makes code much easier to understand. In -some cases there might be good arguments for changing certain -style rules, but we nonetheless keep things as they are in -order to preserve consistency.
++Most open-source projects developed by +Google conform to the requirements in this guide. +
-Another issue this guide addresses is that of C++ feature -bloat. C++ is a huge language with many advanced features. In -some cases we constrain, or even ban, use of certain features. -We do this to keep code simple and to avoid the various common -errors and problems that these features can cause. This guide -lists these features and explains why their use is -restricted.
-Open-source projects -developed by 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.
+Why do we have this document?
+ +There are a few core goals that we believe this guide should +serve. These are the fundamental whys that +underlie all of the individual rules. By bringing these ideas to +the fore, we hope to ground discussions and make it clearer to our +broader community why the rules are in place and why particular +decisions have been made. If you understand what goals each rule is +serving, it should be clearer to everyone when a rule may be waived +(some can be), and what sort of argument or alternative would be +necessary to change a rule in the guide.
+ +The goals of the style guide as we currently see them are as follows:
+goto
contravenes many
+of the following principles, but is already vanishingly rare, so the Style
+Guide doesn’t discuss it.std::unique_ptr
+demonstrates the ownership transfer unambiguously at the call
+site). #include
s only work properly when your code is
+consistent with the expectations of the tooling. In many cases, rules
+that are attributed to "Be Consistent" boil down to "Just pick one and
+stop worrying about it"; the potential value of allowing flexibility
+on these points is outweighed by the cost of having people argue over
+them. The intent of this document is to provide maximal guidance with +reasonable restriction. As always, common sense and good taste should +prevail. By this we specifically refer to the established conventions +of the entire Google C++ community, not just your personal preferences +or those of your team. Be skeptical about and reluctant to use +clever or unusual constructs: the absence of a prohibition is not the +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.
+ +You may forward declare ordinary classes in order to avoid
-unnecessary #include
s.
Avoid using forward declarations where possible.
+ Just #include
the headers you need.
A "forward declaration" is a declaration of a class,
-function, or template without an associated definition.
-#include
lines can often be replaced with
-forward declarations of whatever symbols are actually
-used by the client code.
#include
s force the
- compiler to open more files and process more
- input.#include
s force the compiler to open
+ more files and process more input.#include
s can force
+ your code to be recompiled more often, due to unrelated
+ changes in the header.std::
yields undefined behavior.#include
is needed
- for a given piece of code, particularly when implicit
- conversion operations are involved. In extreme cases,
- replacing an #include
with a forward
+ declaration or a full #include
is needed.
+ Replacing an #include
with a forward
declaration can silently change the meaning of
- code.// b.h: + struct B {}; + struct D : B {}; + + // good_user.cc: + #include "b.h" + void f(B*); + void f(void*); + void test(D* x) { f(x); } // calls f(B*) ++ If the
#include
was replaced with forward
+ decls for B
and D
,
+ test()
would call f(void*)
.
+
#include
ing the header.std::
usually yields undefined
- behavior.#include
that header.#include
its header file.#include
the
- appropriate header.#include
.When defining a function, parameter order is: inputs, then -outputs.
-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 non-const
pointers. 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 -waters, and, as always, consistency with related -functions may require you to bend the rule.
- -You should include all the headers that define the symbols you rely
-upon (except in cases of forward
-declaration). If you rely on symbols from bar.h
,
+upon, except in the unusual case of forward
+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
@@ -430,12 +505,11 @@ system-specific code small and localized. Example:
Unnamed namespaces in .cc
files are
-encouraged. With named namespaces, choose the name based on
-the
-project, and possibly its
-path. Do not use a using-directive.
-Do not use inline namespaces.
With few exceptions, place code in a namespace. Namespaces
+should have unique names based on the project name, and possibly
+its path. Unnamed namespaces in .cc
files are
+encouraged. Do not use using-directives. Do not use
+inline namespaces.
Namespaces provide a (hierarchical) axis of naming, in -addition to the (also hierarchical) name axis provided by -classes.
+Namespaces provide a method for preventing name conflicts +in large programs while allowing most code to use reasonably +short names.
For example, if two different projects have a class
Foo
in the global scope, these symbols may
collide at compile time or at runtime. If each project
-places their code in a namespace,
-project1::Foo
and project2::Foo
-are now distinct symbols that do not collide.
project1::Foo
+and project2::Foo
are now distinct symbols that
+do not collide, and code within each project's namespace
+can continue to refer to Foo
without the prefix.
Inline namespaces automatically place their names in the enclosing scope. Consider the following snippet, for @@ -478,10 +553,9 @@ across versions.
Namespaces can be confusing, because they provide an -additional (hierarchical) axis of naming, in addition to -the (also hierarchical) name axis provided by -classes.
+Namespaces can be confusing, because they complicate +the mechanics of figuring out what definition a name refers +to.
Inline namespaces, in particular, can be confusing because names aren't actually restricted to the namespace @@ -491,13 +565,18 @@ some larger versioning policy.
Use of unnamed namespaces in header files can easily cause violations of the C++ One Definition Rule (ODR).
+ +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.
Use namespaces according to the policy described below. Terminate namespaces with comments as shown in the -given examples.
+given examples. See also the rules on +Namespace Names.However, file-scope declarations that are - associated with a particular class may be declared in - that class as types, static data members or static - member functions rather than as members of an unnamed - namespace.
.h
@@ -539,11 +612,12 @@ bool UpdateInternals(Frobber* f, int newval) {
Namespaces wrap the entire source file after includes, - gflags definitions/declarations, and - forward declarations of classes from other namespaces:
+ gflags definitions/declarations + and forward declarations of classes from other namespaces.// In the .h file namespace mynamespace { @@ -570,30 +644,27 @@ void MyClass::Foo() { } // namespace mynamespace-
The typical .cc
file might have more
- complex detail, including the need to reference
- classes in other namespaces.
More complex .cc
files might have additional details,
+ like flags or using-declarations.
#include "a.h" DEFINE_bool(someflag, false, "dummy flag"); -class C; // Forward declaration of class C in the global namespace. -namespace a { class A; } // Forward declaration of a::A. +using ::foo::bar; -namespace b { +namespace a { -...code for b... // Code goes against the left margin. +...code for a... // Code goes against the left margin. -} // namespace b +} // namespace a
std
, not even forward declarations of
+ std
, including forward declarations of
standard library classes. Declaring entities in
namespace std
is undefined behavior, i.e.,
not portable. To declare entities from the standard
@@ -608,118 +679,59 @@ using namespace foo;
You may use a using-declaration
- anywhere in a .cc
file, and in functions,
- methods or classes in .h
files.
.cc
file (including in
+ the global namespace), and in functions,
+ methods, classes, or within internal namespaces in
+ .h
files.
+
+ Do not use using-declarations in .h
+ files except in explicitly marked internal-only
+ namespaces, because anything imported into a namespace
+ in a .h
file becomes part of the public
+ API exported by that file.
// OK in .cc files. -// Must be in a function, method or class in .h files. +// Must be in a function, method, internal namespace, or +// class in .h files. using ::foo::bar;
Namespace aliases are allowed anywhere in a
- .cc
file, anywhere inside the named namespace
- that wraps an entire .h
file, and in
- functions and methods.
Namespace aliases are allowed anywhere where
+ a using-declaration is allowed. In particular,
+ namespace aliases should not be used at namespace scope
+ in .h
files except in explicitly marked
+ internal-only namespaces.
// Shorten access to some commonly used names in .cc files. -namespace fbz = ::foo::bar::baz; +namespace baz = ::foo::bar::baz; +-// Shorten access to some commonly used names (in a .h file). +
// Shorten access to some commonly used names (in a .h file). namespace librarian { -// The following alias is available to all files including -// this header (in namespace librarian): -// alias names should therefore be chosen consistently -// within a project. -namespace pd_s = ::pipeline_diagnostics::sidetable; +namespace impl { // Internal, not part of the API. +namespace sidetable = ::pipeline_diagnostics::sidetable; +} // namespace impl inline void my_inline_function() { // namespace alias local to a function (or method). - namespace fbz = ::foo::bar::baz; + namespace baz = ::foo::bar::baz; ... } } // namespace librarian-
Note that an alias in a .h file is visible to - everyone #including that file, so public headers - (those available outside a project) and headers - transitively #included by them, should avoid defining - aliases, as part of the general goal of keeping - public APIs as small as possible.
-Although you may use public nested classes when they are -part of an interface, consider a namespace -to keep declarations out of the global scope.
-A class can define another class within it; this is also -called a member class.
- - -class Foo { - - private: - // Bar is a member class, nested within Foo. - class Bar { - ... - }; - -}; --
This is useful when the nested (or member) class is only
-used by the enclosing class; making it a member puts it
-in the enclosing class scope rather than polluting the
-outer scope with the class name. Nested classes can be
-forward declared within the enclosing class and then
-defined in the .cc
file to avoid including
-the nested class definition in the enclosing class
-declaration, since the nested class definition is usually
-only relevant to the implementation.
Nested classes can be forward-declared only within the
-definition of the enclosing class. Thus, any header file
-manipulating a Foo::Bar*
pointer will have
-to include the full class declaration for
-Foo
.
Do not make nested classes public unless they are -actually part of the interface, e.g., a class that holds a -set of options for some method.
-Prefer nonmember functions within a namespace or static -member functions to global functions; use completely global -functions rarely.
+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 defined in the same compilation unit as -production classes may introduce unnecessary coupling and -link-time dependencies when directly called from other -compilation units; static member functions are -particularly susceptible to this. Consider extracting a -new class, or placing the functions in a namespace -possibly in a separate library.
+namespaces instead. For a header +myproject/foo_bar.h
, for example, write
+namespace myproject { +namespace foo_bar { +void Function1(); +void Function2(); +} +} ++
instead of
+namespace myproject { +class FooBar { + public: + static void Function1(); + static void Function2(); +}; +} +
If you must define a nonmember function and it is only
needed in its .cc
file, use an unnamed
@@ -829,11 +850,11 @@ for (int i = 0; i < 1000000; ++i) {
Static or global variables of class type 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.
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.
Avoid doing complex initialization in constructors (in -particular, initialization that can fail or that requires -virtual method calls).
+Avoid virtual method calls in constructors, and avoid +initialization that can fail if you can't signal an error.
It is possible to perform initialization in the body +
It is possible to perform arbitrary initialization in the body of the constructor.
Convenience in typing. No need to worry about whether the -class has been initialized or not.
+const
and may also be easier to use with standard containers
+ or algorithms.The problems with doing work in constructors are:
main()
, possibly breaking some implicit
- assumptions in the constructor code. For instance,
-
-
- gflags
- will not yet have been initialized.bool
+ IsValid()
state checking mechanism (or similar) which is easy
+ to forget to call.Constructors should never call virtual functions or
-attempt to raise non-fatal failures. If your object requires
-non-trivial initialization, consider using
- a factory function or Init()
-method.
Constructors should never call virtual functions. If appropriate
+for your code
+,
+terminating the program may be an appropriate error handling
+response. Otherwise, consider a factory function
+or Init()
method. 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).
If your class defines member variables, you must provide an -in-class initializer for every member variable or write a -constructor (which can be a default constructor). If you do -not declare any constructors yourself then the compiler -will generate a default constructor for you, which may -leave some fields uninitialized or initialized to -inappropriate values.
+Do not define implicit conversions. Use the explicit
+keyword for conversion operators and single-argument
+constructors.
The default constructor is called when we
-new
a class object with no arguments. It is always
-called when calling new[]
(for arrays). In-class
-member initialization means declaring a member variable using a
-construction like int count_ = 17;
or
-string name_{"abc"};
, as opposed to just
-int count_;
or string name_;
.
Implicit conversions allow an
+object of one type (called the source type) to
+be used where a different type (called the destination
+type) is expected, such as when passing an
+int
argument to a function that takes a
+double
parameter.
A user-defined default constructor is used to -initialize an object if no initializer is provided. It -can ensure that an object is always in a valid and usable -state as soon as it's constructed; it can also ensure -that an object is initially created in an obviously -"impossible" state, to aid debugging.
+In addition to the implicit conversions defined by the language,
+users can define their own, by adding appropriate members to the
+class definition of the source or destination type. An implicit
+conversion in the source type is defined by a type conversion operator
+named after the destination type (e.g. operator
+bool()
). An implicit conversion in the destination
+type is defined by a
In-class member initialization ensures that a member -variable will be initialized appropriately without having -to duplicate the initialization code in multiple -constructors. This can reduce bugs where you add a new -member variable, initialize it in one constructor, and -forget to put that initialization code in another -constructor.
-The explicit
keyword can be applied to a constructor
+or (since C++11) a conversion operator, to ensure that it can only be
+used when the destination type is explicit at the point of use,
+e.g. with a cast. This applies not only to implicit conversions, but to
+C++11's list initialization syntax:
class Foo { + explicit Foo(int x, double y); + ... +}; --- -Explicitly defining a default constructor is extra -work for you, the code writer.
- -In-class member initialization is potentially -confusing if a member variable is initialized as part of -its declaration and also initialized in a constructor, -since the value in the constructor will override the -value in the declaration.
--- -Use in-class member initialization for simple -initializations, especially when a member variable must -be initialized the same way in more than one -constructor.
- -If your class defines member variables that aren't -initialized in-class, and if it has no other -constructors, you must define a default constructor (one -that takes no arguments). It should preferably initialize -the object in such a way that its internal state is -consistent and valid.
- -The reason for this is that if you have no other -constructors and do not define a default constructor, the -compiler will generate one for you. This compiler -generated constructor may not initialize your object -sensibly.
- -If your class inherits from an existing class but you -add no new member variables, you are not required to have -a default constructor.
-
Use the C++ keyword explicit
for constructors
-callable with one argument.
Normally, if a
-constructor can be called with one argument, it can be used as a
-conversion. For instance, if you define
-Foo::Foo(string name)
and then pass a string
-to a function that expects a Foo
, the
-constructor will be called to convert the string into a
-Foo
and will pass the Foo
to
-your function for you. This can be convenient but is also
-a source of trouble when things get converted and new
-objects created without you meaning them to. Declaring a
-constructor explicit
prevents it from being
-invoked implicitly as a conversion.
In addition to single-parameter constructors, this also
-applies to constructors where every parameter after the
-first has a default value, e.g.,
-Foo::Foo(string name, int id = 42)
.
Avoids undesirable conversions.
-None.
-We require all constructors that are callable with
-a single argument to be
-explicit. Always put explicit
in front of
-such constructors in the class definition:
-explicit Foo(string name);
Copy and move constructors are exceptions: they should not be
-explicit
. Classes that are intended to be transparent
-wrappers around other classes are also exceptions.
-Such exceptions should be clearly marked with
-comments.
Finally, constructors that take only a
-std::initializer_list
may be non-explicit. This permits
-construction of your type from a braced initializer list, as in an assignment-style initialization,
-function argument, or return statement. For example:
MyType m = {1, 2}; - MyType MakeMyType() { return {1, 2}; } - TakeMyType({1, 2}); +void Func(Foo f);+
Func({42, 3.14}); // Error ++This kind of code isn't technically an implicit conversion, but the +language treats it as one as far as
explicit
is concerned.
+explicit
, there's no reliable way to tell whether
+ it's intended to define an implicit conversion, or the author
+ simply forgot to mark it.Type conversion operators, and constructors that are
+callable with a single argument, must be marked
+explicit
in the class definition. As an
+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
+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
+take a single std::initializer_list
parameter should
+also omit explicit
, in order to support copy-initialization
+(e.g. MyType m = {1, 2};
).
Make your type copyable/movable if it will be useful, and if it -makes sense in the context of the rest of the API. -As a rule of thumb, if the behavior (including computational -complexity) of a copy isn't immediately obvious to users of your type, -your type shouldn't be copyable. If you choose to make it copyable, -define both copy operations (constructor and assignment). If your -type is copyable and a move operation is more efficient than a copy, -define both move operations (constructor and assignment). -If your type is not copyable, but the correctness of a move is obvious -to users of the type and its fields support it, you may make the type -move-only by defining both of the move operations. +
Make your type copyable/movable if it will be useful, and if it makes sense +in the context of the rest of the API. As a rule of thumb, if the behavior +(including computational complexity) of a copy isn't immediately obvious to +users of your type, your type shouldn't be copyable. If you define a copy or +move constructor, define the corresponding assignment operator, and vice-versa. +If your type is copyable, do not define move operations unless they are +significantly more efficient than the corresponding copy operations. If your +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.
-Prefer to define copy and move operations with = default
.
-Defining non-default move operations currently requires a style
-exception. Remember to review the correctness of any defaulted
-operations as you would any other code.
-
If your type provides copy operations, it is recommended that you design
+your class so that the default implementation of those operations is correct
+and that you explicitly define them with = default
. Remember to
+review the correctness of any defaulted operations as you would any other
+code.
class Foo { // Copyable and movable type. + public: + Foo(Foo&& other) = default; + Foo(const Foo& other) = default; + Foo& operator=(Foo&& other) = default; + Foo& operator=(const Foo& other) = default; + + private: + string field_; +}; ++ +
class Foo { + public: + Foo(Foo&& other) : field_(other.field) {} + // Bad, defines only move constructor, but not operator=. + + private: + Field field_; +}; +
Due to the risk of slicing, avoid providing an assignment operator or public copy/move constructor for a class that's @@ -1249,7 +1265,7 @@ syntax. For example:
... } -X::X() : X("") { } +X::X() : X("") {}Inheriting constructors allow a derived class to have
@@ -1547,95 +1563,134 @@ to end with Interface
.
Do not overload operators except in rare, special -circumstances. Do not create user-defined literals.
+Overload operators judiciously. Do not create user-defined literals.
A class can
-define that operators such as +
and
-/
operate on the class as if it were a
-built-in type. An overload of operator""
-allows the built-in literal syntax to be used to create
-objects of class types.
C++ permits user code to
+declare
+overloaded versions of the built-in operators using the
+operator
keyword, so long as one of the parameters
+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 appear more
-intuitive because a class will behave in the same way as
-built-in types (such as int
). Overloaded
-operators are more playful names for functions that are
-less-colorfully named, such as Equals()
or
-Add()
.
For some template functions to work correctly, you may -need to define operators.
+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
+for certain operations (e.g. ==
, <
,
+=
, and <<
), and adhering to
+those conventions can make user-defined types more readable
+and enable them to interoperate with libraries that expect
+those names.
User-defined literals are a very concise notation for creating objects of user-defined types.
While operator overloading can make code more intuitive, -it has several drawbacks:
-Equals()
is much easier than searching for
- relevant invocations of ==
.Foo + 4
may do one
- thing, while &Foo + 4
does something
- totally different. The compiler does not complain for
- either of these, making this very hard to debug.foo < bar
+ may do one thing, while &foo < &bar
+ does something totally different.&
can cause the same
+ code to have different meanings depending on whether
+ the overload declaration is visible. Overloads of
+ &&
, ||
, and ,
+ (comma) cannot match the evaluation-order semantics of the
+ built-in operators.Overloading also has surprising ramifications. For
-instance, if a class overloads unary
-operator&
, it cannot safely be
-forward-declared.
In general, do not overload operators. You can define
-ordinary functions like Equals()
if
-you need them. Likewise, avoid the dangerous unary
-operator&
at all costs, if there's any
-possibility the class might be forward-declared.
Define overloaded operators only if their meaning is
+obvious, unsurprising, and consistent with the corresponding
+built-in operators. For example, use |
as a
+bitwise- or logical-or, not as a shell-style pipe.
Do not overload operator""
, i.e. do not
-introduce user-defined literals.
Define operators only on your own types. More precisely,
+define them in the same headers, .cc files, and namespaces
+as the types they operate on. That way, the operators are available
+wherever the type is, minimizing the risk of multiple
+definitions. If possible, avoid defining operators as templates,
+because they must satisfy this rule for any possible template
+arguments. If you define an operator, also define
+any related operators that make sense, and make sure they
+are defined consistently. For example, if you overload
+<
, overload all the comparison operators,
+and make sure <
and >
never
+return true for the same arguments.
However, there may be rare cases where you need to
-overload an operator to interoperate with templates or
-"standard" C++ classes (such as
-operator<<(ostream&, const T&)
-for logging). These are acceptable if fully justified, but you should try to avoid these
-whenever possible. In particular, do not overload
-operator==
or operator<
just
-so that your class can be used as a key in an STL
-container; instead, you should create equality and
-comparison functor types when declaring the
-container.
Prefer to define non-modifying binary operators as
+non-member functions. If a binary operator is defined as a
+class member, implicit conversions will apply to the
+right-hand argument, but not the left-hand one. It will
+confuse your users if a < b
compiles but
+b < a
doesn't.
Some of the STL algorithms do require you to overload
-operator==
, and you may do so in these
-cases, provided you document why.
Don't go out of your way to avoid defining operator
+overloads. For example, prefer to define ==
,
+=
, and <<
, rather than
+Equals()
, CopyFrom()
, and
+PrintTo()
. Conversely, don't define
+operator overloads just because other libraries expect
+them. For example, if your type doesn't have a natural
+ordering, but you want to store it in a std::set
,
+use a custom comparator rather than overloading
+<
.
See also Copyable and Movable -Types and Function 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.
+The =
operator is covered in the section on
+copy constructors. Overloading
+<<
for use with streams is covered in the
+section on streams. See also the rules on
+function overloading, which
+apply to operator overloading as well.
Make data members private
, and provide access
-to them through accessor functions as needed (for technical
+
Make data members private
, unless they are
+static const
(and follow the
+naming convention for constants). For technical
reasons, we allow data members of a test fixture class to
be protected
when using
Google
-Test). Typically a variable would be called
-foo_
and the accessor function
-foo()
. You may also want a mutator function
-set_foo()
. Exception: static
-const
data members (typically called
-kFoo
) need not be private
.
The definitions of accessors are usually inlined in -the header file.
- -See also Inheritance and -Function Names.
- -Friend declarations should always be in the private
-section. If copying and assignment are disabled with a macro
-such as DISALLOW_COPY_AND_ASSIGN
, it should be
+
If copying and assignment are disabled with a macro such as
+DISALLOW_COPY_AND_ASSIGN
, it should be
at the end of the private:
section, and should be
the last thing in the class. See
Copyable and Movable Types.
Method definitions in the corresponding
-.cc
file should be the same as the
-declaration order, as much as possible.
Do not put large method definitions inline in the class definition. Usually, only trivial or performance-critical, and very short, methods may be @@ -1722,6 +1758,34 @@ Functions for more details.
When defining a function, parameter order is: inputs, then +outputs.
+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 non-const
pointers. 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 +waters, and, as always, consistency with related +functions may require you to bend the rule.
+ +All parameters passed by 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); ++ +
In fact it is a very strong convention in Google code
+that input arguments are values or const
+references while output arguments are pointers. Input
+parameters may be const
pointers, but we
+never allow non-const
reference parameters
+except when required by convention, e.g.,
+swap()
.
However, there are some instances where using
+const T*
is preferable to const
+T&
for input parameters. For example:
Remember that most of the time input
+parameters are going to be specified as const
+T&
. Using const T*
instead
+communicates to the reader that the input is somehow
+treated differently. So if you choose const
+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.
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
+takes const char*
.
class MyClass { + public: + void Analyze(const 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.
+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.
+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
+vector
so that the user can use an
+initializer list
+ to specify the arguments.
We do not allow default function parameters, except in +limited situations as explained below. Simulate them with +function overloading instead, if appropriate.
+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 +define many functions for the rare exceptions. Compared +to overloading the function, default arguments have a +cleaner syntax, with less boilerplate and a clearer +distinction between 'required' and 'optional' +arguments.
+Function pointers are confusing in the presence of +default arguments, since the function signature often +doesn't match the call signature. Adding a default +argument to an existing function changes its type, which +can cause problems with code taking its address. Adding +function overloads avoids these problems. In addition, +default parameters may result in bulkier code since they +are replicated at every call-site -- as opposed to +overloaded functions, where "the default" appears only in +the function definition.
+While the cons above are not that onerous, they still +outweigh the (small) benefits of default arguments over +function overloading. So except as described below, we +require all arguments to be explicitly specified.
+ +One specific exception is when the function is a +static function (or in an unnamed namespace) in a .cc +file. In this case, the cons don't apply since the +function's use is so localized.
+ +In addition, default function parameters are allowed in +constructors. Most of the cons listed above don't apply to +constructors because it's impossible to take their address.
+ +Another specific exception is when default arguments +are used to simulate variable-length argument lists.
+ + +// Support up to 4 params by using a default empty AlphaNum. +string StrCat(const AlphaNum &a, + const AlphaNum &b = gEmptyAlphaNum, + const AlphaNum &c = gEmptyAlphaNum, + const AlphaNum &d = gEmptyAlphaNum); ++
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); ++
The new form, introduced in C++11, uses the auto
+ keyword before the function name and a trailing return type after
+ the argument list. For example, the declaration above could
+ equivalently be written:
auto foo(int x) -> int; ++
The trailing return type is in the function's scope. This doesn't
+ 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, + but not in all cases. Even when the compiler can deduce it automatically, + sometimes specifying it explicitly would be clearer for readers. +
+Sometimes it's easier and more readable to specify a return type + 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);+ versus +
template <class T, class 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 like C and Java, so some readers may + find it unfamiliar.
+Existing code bases have an enormous number of function + 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 + required (such as lambdas) or where, by putting the type after the + function's parameter list, it allows you to write the type in a much + more readable way. The latter case should be rare; it's mostly an + issue in fairly complicated template code, which is + discouraged in most cases.
+ +*
and
->
operators. Some smart pointer types
can be used to automate ownership bookkeeping, to ensure
these responsibilities are met.
-
+
std::unique_ptr
is a smart pointer type
introduced in C++11, which expresses exclusive ownership
of a dynamically allocated object; the object is deleted
when the std::unique_ptr
goes out of scope.
It cannot be copied, but can be moved to
represent ownership transfer.
-
+
std::shared_ptr
is a smart pointer type
that expresses shared ownership of
a dynamically allocated object. std::shared_ptr
s
@@ -1935,81 +2248,11 @@ you can download
All parameters passed by 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); -- -
In fact it is a very strong convention in Google code
-that input arguments are values or const
-references while output arguments are pointers. Input
-parameters may be const
pointers, but we
-never allow non-const
reference parameters
-except when required by convention, e.g.,
-swap()
.
However, there are some instances where using
-const T*
is preferable to const
-T&
for input parameters. For example:
Remember that most of the time input
-parameters are going to be specified as const
-T&
. Using const T*
instead
-communicates to the reader that the input is somehow
-treated differently. So if you choose const
-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.
Use rvalue references only to define move constructors and move
-assignment operators. Do not
-use std::forward
.
+
Use rvalue references only to define move constructors and move assignment +operators, or for perfect forwarding.
Use rvalue references only to define move constructors and move
-assignment operators, as described in
-Copyable and Movable Types.
-Do not use std::forward
utility function. You may
-use std::move
to express moving a value from one object
-to another rather than copying it.
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
-takes const char*
.
class MyClass { - public: - void Analyze(const 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.
-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.
-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()
.
We do not allow default function parameters, except in -limited situations as explained below. Simulate them with -function overloading instead, if appropriate.
-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 -define many functions for the rare exceptions. Compared -to overloading the function, default arguments have a -cleaner syntax, with less boilerplate and a clearer -distinction between 'required' and 'optional' -arguments.
-Function pointers are confusing in the presence of -default arguments, since the function signature often -doesn't match the call signature. Adding a default -argument to an existing function changes its type, which -can cause problems with code taking its address. Adding -function overloads avoids these problems. In addition, -default parameters may result in bulkier code since they -are replicated at every call-site -- as opposed to -overloaded functions, where "the default" appears only in -the function definition.
-While the cons above are not that onerous, they still -outweigh the (small) benefits of default arguments over -function overloading. So except as described below, we -require all arguments to be explicitly specified.
- -One specific exception is when the function is a -static function (or in an unnamed namespace) in a .cc -file. In this case, the cons don't apply since the -function's use is so localized.
- -In addition, default function parameters are allowed in -constructors. Most of the cons listed above don't apply to -constructors because it's impossible to take their address.
- -Another specific exception is when default arguments -are used to simulate variable-length argument lists.
- - -// Support up to 4 params by using a default empty AlphaNum. -string StrCat(const AlphaNum &a, - const AlphaNum &b = gEmptyAlphaNum, - const AlphaNum &c = gEmptyAlphaNum, - const AlphaNum &d = gEmptyAlphaNum); -+
Use rvalue references only to define move constructors and move assignment
+ operators (as described in Copyable and
+ Movable Types) and, in conjunction with std::forward
,
+to support perfect forwarding. You may use std::move
to express
+moving a value from one object to another rather than copying it.
Use a safe allocator instead, such as
-std::vector
or
+vector
or
std::unique_ptr<T[]>
.
Use streams only for logging.
+Use streams where appropriate, and stick to "simple" +usages.
Streams are a replacement for printf()
-and scanf()
.
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
+and test diagnostics.
With streams, you do not need to know the type of the
-object you are printing. You do not have problems with
-format strings not matching the argument list. (Though
-with gcc, you do not have that problem with
-printf
either.) Streams have automatic
-constructors and destructors that open and close the
-relevant files.
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,
+and is very difficult to use portably.
+printf
also obliges you to choose among the
+numerous slightly different versions of that function,
+and navigate the dozens of conversion specifiers.
Streams provide first-class support for console I/O
+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.
Streams make it difficult to do functionality like
-pread()
. Some formatting (particularly the
-common format string idiom %.*s
) is
-difficult if not impossible to do efficiently using
-streams without using printf
-like hacks.
-Streams do not support operator reordering (the
-%1$s
directive), which is helpful for
-internationalization.
<<
operators interferes with
+internationalization, because it bakes word order into the
+code, and streams' support for localization is
+flawed.<<
is
+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 +end-users. Be consistent with the code around you, and with the +codebase as a whole; if there's an established tool for +your problem, use that tool instead.
+Avoid using streams for I/O that faces external users or +handles untrusted data. Instead, find and use the appropriate +templating libraries to handle issues like internationalization, +localization, and security hardening.
-Do not use streams, except where
-required by a logging interface. Use
-printf
-like routines instead.
If you do use streams, avoid the stateful parts of the
+streams API (other than error state), such as imbue()
,
+xalloc()
, and register_callback()
.
+Use explicit formatting functions rather than
+stream manipulators or formatting flags to control formatting
+details such as number base, precision, or padding.
There are various pros and cons to using streams, but -in this case, as in many other cases, consistency trumps -the debate. Do not use streams in your code.
-There has been debate on this issue, so this explains
-the reasoning in greater depth. Recall the Only One Way
-guiding principle: we want to make sure that whenever we
-do a certain type of I/O, the code looks the same in all
-those places. Because of this, we do not want to allow
-users to decide between using streams or using
-printf
plus Read/Write/etc. Instead, we
-should settle on one or the other. We made an exception
-for logging because it is a pretty specialized
-application, and for historical reasons.
Proponents of streams have argued that streams are the -obvious choice of the two, but the issue is not actually -so clear. For every advantage of streams they point out, -there is an equivalent disadvantage. The biggest -advantage is that you do not need to know the type of the -object to be printing. This is a fair point. But, there -is a downside: you can easily use the wrong type, and the -compiler will not warn you. It is easy to make this kind -of mistake without knowing when using streams.
- -cout << this; // Prints the address -cout << *this; // Prints the contents -- -
The compiler does not generate an error because
-<<
has been overloaded. We discourage
-overloading for just this reason.
Some say printf
formatting is ugly and
-hard to read, but streams are often no better. Consider
-the following two fragments, both with the same typo.
-Which is easier to discover?
cerr << "Error connecting to '" << foo->bar()->hostname.first - << ":" << foo->bar()->hostname.second << ": " << strerror(errno); - -fprintf(stderr, "Error connecting to '%s:%u: %s", - foo->bar()->hostname.first, foo->bar()->hostname.second, - strerror(errno)); -- -
And so on and so forth for any issue you might bring -up. (You could argue, "Things would be better with the -right wrappers," but if it is true for one scheme, is it -not also true for the other? Also, remember the goal is -to make the language smaller, not add yet more machinery -that someone has to learn.)
- -Either path would yield different advantages and
-disadvantages, and there is not a clearly superior
-solution. The simplicity doctrine mandates we settle on
-one of them though, and the majority decision was on
-printf
+
-read
/write
.
Overload <<
as a streaming operator
+for your type only if your type represents a value, and
+<<
writes out a human-readable string
+representation of that value. Avoid exposing implementation
+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).
inttypes.h
):
int64_t
%qd
, %lld
%"PRId64"
%" PRId64 "
uint64_t
%qu
, %llu
,
%llx
%"PRIu64"
,
- %"PRIx64"
%" PRIu64 "
,
+ %" PRIx64 "
size_t
%u
%"PRIuS"
, %"PRIxS"
%" PRIuS "
, %" PRIxS "
%zu
ptrdiff_t
%d
%"PRIdS"
%" PRIdS "
%td
%
when using
the PRI*
macros. So, e.g.
- printf("x = %30"PRIuS"\n", x)
would
+ 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)
.
@@ -3094,11 +3226,6 @@ problems of printing, comparisons, and structure alignment.
uint64_t my_mask = 3ULL << 48;
-
- #ifdef _LP64
to choose
- between the code variants. (But please avoid this if
- possible, and keep any such changes localized.)Use auto
to avoid type names that are just
clutter. Continue to use manifest type declarations when it
helps readability, and never use auto
for
-anything but local variables.
The auto
keyword is also used in an
+unrelated C++11 feature: it's part of the syntax for a
+new kind of function declaration with a
+trailing return type.
C++ type names can sometimes be long and cumbersome,
especially when they involve templates or namespaces. In
@@ -3296,11 +3429,13 @@ if x
was declared hundreds of lines earlier.
The interaction between auto
and C++11
-brace-initialization can be confusing. The
-declarations:
auto x(3); // Note: parentheses. -auto y{3}; // Note: curly braces. +auto x{3}; +auto y = {3};mean different things — @@ -3323,13 +3458,7 @@ only. Do not use
auto
for file-scope or namespace-scope variables, or for class members. Never initialize anauto
-typed variable with a braced initializer list. - -The
-auto
keyword is also used in an -unrelated C++11 feature: it's part of the syntax for a -new kind of function declaration with a trailing return -type. Trailing return types are permitted only in lambda -expressions.
Use lambda expressions where appropriate. Do not use -default lambda captures; write all captures explicitly.
+Use lambda expressions where appropriate. Avoid
+default lambda captures when capturing this
or
+if the lambda will escape the current scope.
this
if any members are used. The default capture must come
+first and be one of &
for capture by reference or
+=
for capture by value, followed by any explicit captures which
+differ from the default:
+
+int weight = 3; +int sum = 0; +// Captures `weight` (implicitly) by value and `sum` by reference. +std::for_each(v.begin(), v.end(), [=, &sum](int x) { + sum += weight * x; +}); ++
Lambdas were introduced in C++11 along with a set of utilities
for working with function objects, such as the polymorphic
wrapper std::function
.
@@ -3461,6 +3607,10 @@ wrapper std::function
.
algorithms, which can be a readability
improvement.
+
std::function
, and
std::bind
can be used in combination as a
general purpose callback mechanism; they make it easy
@@ -3471,8 +3621,13 @@ wrapper std::function
.
this
.
+ std::function
.
[=](int x) { return x + n; }
-you should write [n](int x) { return x + n; }
so that
-readers can see immediately that n
is being captured
-(by value).this
or if the lambda will escape the
+current scope. For example, instead of:
+{ + Foo foo; + ... + executor->Schedule([&] { Frobnicate(foo); }) + ... +} +// BAD! `Frobnicate` may be a member function and `foo` may be destroyed +// by the time the lambda runs. ++prefer to write: +
{ + Foo foo; + ... + executor->Schedule([&foo] { Frobnicate(foo); }) + ... +} +// GOOD - The lambda cannot accidentally capture `this` and +// the explicit by-reference capture is more obvious and therefore +// more likely to be checked for correctness. ++
The - + Boost library collection is a popular collection of peer-reviewed, free, open-source C++ libraries.
boost/call_traits.hpp
boost/compressed_pair.hpp
boost/graph
,
except serialization (adj_list_serialize.hpp
) and
parallel/distributed algorithms and data structures
(boost/graph/parallel/*
and
boost/graph/distributed/*
).boost/property_map
, except
parallel/distributed property maps (boost/property_map/parallel/*
).boost/iterator/iterator_adaptor.hpp
,
- boost/iterator/iterator_facade.hpp
, and
- boost/function_output_iterator.hpp
boost/iterator
boost/polygon/voronoi_diagram.hpp
, and
boost/polygon/voronoi_geometry_type.hpp
boost/bimap
boost/math/distributions
boost/multi_index
boost/heap
boost/container/flat_map
, and
boost/container/flat_set
boost/intrusive
.We are actively considering adding other Boost @@ -3682,14 +3857,14 @@ is discouraged because they've been superseded by standard libraries in C++11:
Use libraries and language extensions from C++11 (formerly -known as C++0x) when appropriate. -Consider portability to other environments -before using C++11 features in your -project.
+Do not define specializations of std::hash
.
std::hash
is easy to use, and simplifies the code
+since you don't have to name it explicitly. Specializing
+std::hash
is the standard way of specifying how to
+hash a type, so it's what outside resources will teach, and what
+new engineers will expect.
std::hash
is hard to specialize. It requires a lot
+of boilerplate code, and more importantly, it combines responsibility
+for identifying the hash inputs with responsibility for executing the
+hashing algorithm itself. The type author has to be responsible for
+the former, but the latter requires expertise that a type author
+usually doesn't have, and shouldn't need. The stakes here are high
+because low-quality hash functions can be security vulnerabilities,
+due to the emergence of
+
+hash flooding attacks.
Even for experts, std::hash
specializations are
+inordinately difficult to implement correctly for compound types,
+because the implementation cannot recursively call std::hash
+on data members. High-quality hash algorithms maintain large
+amounts of internal state, and reducing that state to the
+size_t
bytes that std::hash
+returns is usually the slowest part of the computation, so it
+should not be done more than once.
Due to exactly that issue, std::hash
does not work
+with std::pair
or std::tuple
, and the
+language does not allow us to extend it to support them.
You can use std::hash
with the types that it supports
+"out of the box", but do not specialize it to support additional types.
+If you need a hash table with a key type that std::hash
+does not support, consider using legacy hash containers (e.g.
+hash_map
) for now; they use a different default hasher,
+which is unaffected by this prohibition.
If you want to use the standard hash containers anyway, you will +need to specify a custom hasher for the key type, e.g.
+std::unordered_map<MyKeyType, Value, MyKeyTypeHasher> my_map; +
+Consult with the type's owners to see if there is an existing hasher +that you can use; otherwise work with them to provide one, + or roll your own.
+ +We are planning to provide a hash function that can work with any type,
+using a new customization mechanism that doesn't have the drawbacks of
+std::hash
.
Use libraries and language extensions from C++11 when appropriate. +Consider portability to other environments +before using C++11 features in your +project.
+ +C++11 contains significant changes both to the language and libraries.
auto foo() -> int;
instead of int
- foo();
, because of a desire to preserve
- stylistic consistency with the many existing function
- declarations.<fenv.h>
headers, because many
compilers do not support those features reliably.
-
-
- Function names, variable names, and filenames should be -descriptive; eschew abbreviation.
+Names should be descriptive; eschew abbreviation.
The names of all types — classes, structs, typedefs, -and enums — have the same naming convention. Type names -should start with a capital letter and have a capital letter +enums, and type template parameters — have the same naming convention. +Type names should start with a capital letter and have a capital letter for each new word. No underscores. For example:
// classes and structs @@ -3975,93 +4218,94 @@ variables.Constant Names
--Use a
+k
followed by mixed case, e.g., -kDaysInAWeek
, for constants defined globally or within a class.Variables declared constexpr or const, and whose value is fixed for + the duration of the program, are named with a leading "k" followed + by mixed case. For example:
- -+As a convenience to the reader, compile-time constants of global or class scope -follow a different naming convention from other variables. -Use a
-k
followed by words with uppercase first letters:const int kDaysInAWeek = 7;-This convention may optionally be used for compile-time constants of local scope; -otherwise the usual variable naming rules apply. +
+ +All such variables with static storage duration (i.e. statics and globals, + 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.
Function Names
-Regular functions have mixed case; accessors and mutators -match the name of the variable: -
+MyExcitingFunction()
, -MyExcitingMethod()
, -my_exciting_member_variable()
, -set_my_exciting_member_variable()
.Regular functions have mixed case; "cheap" functions may +use lower case with underscores.
-Regular Functions
+Ordinarily, functions should start with a capital letter and have +a capital letter for each new word (a.k.a. "upper camel case" or "Pascal case"). +Such names should not have underscores. Prefer to capitalize acronyms as +single words (i.e.
-StartRpc()
, notStartRPC()
).Functions should start with a capital letter and have -a capital letter for each new word. No underscores.
- -If your function crashes upon an error, you should -append OrDie to the function name. This only applies to -functions which could be used by production code and to -errors that are reasonably likely to occur during normal -operation.
- -AddTableEntry() +AddTableEntry() DeleteUrl() OpenFileOrDie()-Accessors and Mutators
- -Accessors and mutators (get and set functions) should -match the name of the variable they are getting and -setting. This shows an excerpt of a class whose instance -variable is
+num_entries_
.Functions that are very cheap to call may instead follow the style +for variable names (all lower-case, with underscores between words). +The rule of thumb is that such a function should be so cheap that you +normally wouldn't bother caching its return value when calling it in +a loop. A canonical example is an inline method that just returns one +of the class's member variables.
class MyClass { public: ... - int num_entries() const { return num_entries_; } - void set_num_entries(int num_entries) { num_entries_ = num_entries; } + bool is_empty() const { return num_entries_ == 0; } private: int num_entries_; };- -You may also use lowercase letters for other very -short inlined functions. For example if a function were -so cheap you would not cache the value if you were -calling it in a loop, then lowercase naming would be -acceptable.
-Namespace Names
- - -Namespace names are all lower-case, -and based on project names and possibly their directory -structure:
+Namespace names are all lower-case. Top-level +namespace names are based on the project name +.google_awesome_project
.+@@ -4217,24 +4461,23 @@ author line, consider deleting the author line.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 +subdirectories thereof).
-See Namespaces for a -discussion of namespaces and how to name them.
+ + + + +Keep in mind that the rule +against abbreviated names applies to namespaces just as much +as variable names. Code inside the namespace seldom needs to +mention the namespace name, so there's usually no particular need +for abbreviation anyway.
+ +Naming conventions for nested namespaces are at the discretion +of the team owning the enclosing namespace. If a nested namespace +is used to group related functions in a single header file, consider basing +its name on the header name, e.g. namespace
foo::bar
+for functions defined in the header filebar.h
. +If the nested namespace is being used to distinguish implementation +details from user-facing declarations, one common choice is +internal
.File Contents
Every file should have a comment at the top describing -its contents.
+its contents, unless the specific conditions described below apply. -Generally a
+.h
file will describe the -classes that are declared in the file with an overview of -what they are for and how they are used. A -.cc
file should contain more information -about implementation details or discussions of tricky -algorithms. If you feel the implementation details or a -discussion of the algorithms would be useful for someone -reading the.h
, feel free to put it there -instead, but mention in the.cc
that the -documentation is in the.h
file.A file-level comment in a
-.h
should broadly describe +the contents of the file. If the file declares multiple abstractions, the +file-level comment should document how they are related. A 1 or 2 sentence +file-level comment may be sufficient. The detailed documentation about +individual abstractions belongs with those abstractions, not at the file +level.Do not duplicate comments in both the
+.h
-and the.cc
. Duplicated comments -diverge.Do not duplicate comments in both the
-.h
and the +.cc
. Duplicated comments diverge.A file-level comment may be omitted if the file declares, implements, or +tests exactly one abstraction that is documented by a comment at the point +of declaration.
+ +
// Iterates over the contents of a GargantuanTable. Sample usage: +// Iterates over the contents of a GargantuanTable. +// Example: // GargantuanTableIterator* iter = table->NewIterator(); // for (iter->Seek("foo"); !iter->done(); iter->Next()) { // process(iter->key(), iter->value()); @@ -4256,17 +4500,15 @@ class GargantuanTableIterator { };-If you have already described a class in detail in the -comments at the top of your file feel free to simply -state "See comment at top of file for a complete -description", but be sure to have some sort of -comment.
+The class comment should provide the reader with enough information to know +how and when to use the class, as well as any additional considerations +necessary to correctly use the class. Document the synchronization assumptions +the class makes, if any. If an instance of the class can be accessed by +multiple threads, take extra care to document the rules and invariants +surrounding multithreaded use.
-Document the synchronization assumptions the class -makes, if any. If an instance of the class can be -accessed by multiple threads, take extra care to document -the rules and invariants surrounding multithreaded -use.
+The class comment is often a good place for a small example code snippet +demonstrating a simple and focused usage of the class.
When documenting function overrides, focus on the +specifics of the override itself, rather than repeating +the comment from the overriden function. In many of these +cases, the override needs no additional documentation and +thus no comment is required.
+When commenting constructors and destructors, remember that the person reading your code knows what constructors and destructors are for, so comments that just say @@ -4468,49 +4716,84 @@ vector<string> list{// Comments in braced lists describe the next element DoSomething(); /* For trailing block comments, one space is fine. */ -
When you pass in a null pointer, boolean, or literal -integer values to functions, you should consider adding a -comment about what they are, or make your code -self-documenting by using constants. For example, -compare:
+When the meaning of a function argument is nonobvious, consider +one of the following remedies:
-bool success = CalculateSomething(interesting_value, - 10, - false, - NULL); // What are these arguments?? +
bool
+ argument with an enum
argument. This will make the argument
+ values self-describing.// What are these arguments? +const DecimalNumber product = CalculateProduct(values, 7, false, nullptr);
versus:
-bool success = CalculateSomething(interesting_value, - 10, // Default base value. - false, // Not the first time we're calling this. - NULL); // No callback. -- -
Or alternatively, constants or self-describing variables:
- -const int kDefaultBaseValue = 10; -const bool kFirstTimeCalling = false; -Callback *null_callback = NULL; -bool success = CalculateSomething(interesting_value, - kDefaultBaseValue, - kFirstTimeCalling, - null_callback); +ProductOptions options; +options.set_precision_decimals(7); +options.set_use_cache(ProductOptions::kDontUseCache); +const DecimalNumber product = + CalculateProduct(values, options, /*completion_callback=*/nullptr);Don'ts
-Note that you should never describe the code -itself. Assume that the person reading the code knows C++ -better than you do, even though he or she does not know -what you are trying to do:
+Do not state the obvious. In particular, don't literally describe what +code does, unless the behavior is nonobvious to a reader who understands +C++ well. Instead, provide higher level comments that describe why +the code does what it does, or make the code self describing.
-// Now go through the b array and make sure that if i occurs, -// the next element is i+1. -... // Geez. What a useless comment. +Compare this: + +// Find the element in the vector. <-- Bad: obvious! +auto iter = std::find(v.begin(), v.end(), element); +if (iter != v.end()) { + Process(element); +} ++ +To this: + +// Process "element" unless it was already processed. +auto iter = std::find(v.begin(), v.end(), element); +if (iter != v.end()) { + Process(element); +} ++ +Self-describing code doesn't need a comment. The comment from +the example above would be obvious: + +if (!IsAlreadyProcessed(element)) { + Process(element); +}
80 characters is the maximum.
-If a comment line contains an example -command or a literal URL longer than 80 characters, that -line may be longer than 80 characters for ease of cut and -paste.
+Comment lines can be longer than 80 +characters if it is not feasible to split them without +harming readability, ease of cut and paste or auto-linking +-- e.g. if a line contains an example command or a literal +URL longer than 80 characters.
A raw-string literal may have content that exceeds 80 characters. Except for test code, such literals @@ -4804,6 +5088,11 @@ function call.
Some points to note:
If some parameters are unused, comment out the -variable name in the function definition:
+Unused parameters that are obvious from context may be omitted:
-// Always have named parameters in interfaces. -class Shape { +class Foo { + public: + Foo(Foo&&) = default; + Foo(const Foo&) = default; + Foo& operator=(Foo&&) = default; + Foo& operator=(const Foo&) = default; +}; ++ +Unused parameters that might not be obvious should comment out the variable +name in the function definition:
+ +class Shape { public: virtual void Rotate(double radians) = 0; }; -// Always have named parameters in the declaration. class Circle : public Shape { public: - virtual void Rotate(double radians); + void Rotate(double radians) override; }; -// Comment out unused named parameters in definitions. void Circle::Rotate(double /*radians*/) {}@@ -4879,8 +5172,8 @@ ampersand (&) and the variable name. auto add_to_x = [&x](int n) { x += n; };
Short lambdas may be written inline as function arguments.
-std::set<int> blacklist = {7, 8, 9}; -std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1}; +set<int> blacklist = {7, 8, 9}; +vector<int> digits = {3, 9, 1, 8, 4, 7, 1}; digits.erase(std::remove_if(digits.begin(), digits.end(), [&blacklist](int i) { return blacklist.find(i) != blacklist.end(); }), @@ -4903,7 +5196,7 @@ on each line where appropriate.- -Function calls have the following format:
-bool retval = DoSomething(argument1, argument2, argument3); +bool result = DoSomething(argument1, argument2, argument3);If the arguments do not all fit on one line, they @@ -4911,7 +5204,7 @@ should be broken up onto multiple lines, with each subsequent line aligned with the first argument. Do not add spaces after the open paren or before the close paren:
-bool retval = DoSomething(averyveryveryverylongargument1, +@@ -4941,12 +5235,12 @@ readability due to the complexity or confusing nature of the expressions that make up some arguments, try creating variables that capture those arguments in a descriptive name:bool result = DoSomething(averyveryveryverylongargument1, argument2, argument3);@@ -4921,9 +5214,10 @@ lines with a four space indent: ... ... if (...) { - DoSomething( + bool result = DoSomething( argument1, argument2, // 4 space indent argument3, argument4); + ... }int my_heuristic = scores[x] * y + bases[x]; -bool retval = DoSomething(my_heuristic, x, y, z); +bool result = DoSomething(my_heuristic, x, y, z);Or put the confusing argument on its own line with an explanatory comment:
-bool retval = DoSomething(scores[x] * y + bases[x], // Score heuristic. +bool result = DoSomething(scores[x] * y + bases[x], // Score heuristic. x, y, z);@@ -5461,24 +5755,32 @@ with subsequent lines indented four spaces.-@@ -5783,8 +6085,6 @@ occasionally need to break on Windows:There are two acceptable formats for initializer -lists:
+The acceptable formats for initializer lists are:
-// When it all fits on one line: -MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) {} -+// When everything fits on one line: +MyClass::MyClass(int var) : some_var_(var) { + DoSomething(); +} -or
+// If the signature and initializer list are not all on one line, +// you must wrap before the colon and indent 4 spaces: +MyClass::MyClass(int var) + : some_var_(var), some_other_var_(var + 1) { + DoSomething(); +} -// When it requires multiple lines, indent 4 spaces, putting the colon on -// the first initializer line: +// When the list spans multiple lines, put each member on its own line +// and align them: MyClass::MyClass(int var) : some_var_(var), // 4 space indent some_other_var_(var + 1) { // lined up - ... DoSomething(); - ... } + +// As with any other code block, the close curly can be on the same +// line as the open curly, if it fits. +MyClass::MyClass(int var) + : some_var_(var) {}Parting Words
Use common sense and BE CONSISTENT.
@@ -5811,7 +6111,7 @@ more interesting. Have fun!
-Revision 4.45
-