diff --git a/cppguide.html b/cppguide.html index f517fde..0195cec 100644 --- a/cppguide.html +++ b/cppguide.html @@ -32,36 +32,133 @@ the conventions that govern our C++ code. The term Style is a bit of a misnomer, since these conventions cover far more than just source file formatting.
-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). #includes 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 #includes.
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.
#includes force the
- compiler to open more files and process more
- input.#includes force the compiler to open
+ more files and process more input.#includes 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*).
+
#includeing 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_ptrs
@@ -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 "%zuptrdiff_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 an auto-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.hppboost/compressed_pair.hppboost/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.hppboost/iteratorboost/polygon/voronoi_diagram.hpp, and
boost/polygon/voronoi_geometry_type.hppboost/bimapboost/math/distributionsboost/multi_indexboost/heapboost/container/flat_map, and
boost/container/flat_setboost/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
+kfollowed 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
-kfollowed 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
+.hfile will describe the -classes that are declared in the file with an overview of -what they are for and how they are used. A -.ccfile 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.ccthat the -documentation is in the.hfile.A file-level comment in a
-.hshould 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
-.hand 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,
+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);
+ ...
}
@@ -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:
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.
-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) {}
@@ -5783,8 +6085,6 @@ occasionally need to break on Windows:
-
-
Parting Words
Use common sense and BE CONSISTENT.
@@ -5811,7 +6111,7 @@ more interesting. Have fun!
-Revision 4.45
-