Google C++ Style Guide
Table of Contents
Background
C++ is one of the main development languages used by many of Google's open-source projects. As every C++ programmer knows, the language has many powerful features, but this power brings with it complexity, which in turn can make code more bug-prone and harder to read and maintain.
C++是Google众多开源项目使用一种主要的开发语言。每名C++开发者都清楚,这种语言有许多强大的特性,但也相对增加了复杂性,如果使用不当开发的代码更容易有Bug,并且更难阅读和维护。
The goal of this guide is to manage this complexity by describing in detail the dos and don'ts of writing C++ code. These rules exist to keep the code base manageable while still allowing coders to use C++ language features productively.
本规范通过详细的描述在开发过程的注意事项来克服语言的复杂性。
Style, also known as readability, is what we call 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.
规范常被理解可读性,也就是我们在C++编码过程的约定。
Most 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.
多数Google主导的开源项目,遵守这个规范。但这个规范这不C++教程,在此假定读者熟悉这种语言。
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:
规范的主要目标如下:
Style rules should pull their weight
The benefit of a style rule must be large enough to justify asking all of our engineers to remember it. The benefit is measured relative to the codebase we would get without the rule, so a rule against a very harmful practice may still have a small benefit if people are unlikely to do it anyway. This principle mostly explains the rules we don’t have, rather than the rules we do: for example, goto contravenes many of the following principles, but is already vanishingly rare, so the Style Guide doesn’t discuss it.
规范应该发挥他的作用
遵守规范所带来的好处一定要大于要求每一们开发者记住这项规范。遵守堆满所带的好处与代码规模有关。本文的规则主要是我们没有做的,而不是我们已经做的。比如:尽量不用goto语句,这个大家都已经在做了,所以本规范没有提。
Optimize for the reader, not the writer
Our codebase (and most individual components submitted to it) is expected to continue for quite some time. As a result, more time will be spent reading most of our code than writing it. We explicitly choose to optimize for the experience of our average software engineer reading, maintaining, and debugging code in our codebase rather than ease when writing said code. "Leave a trace for the reader" is a particularly common sub-point of this principle: When something surprising or unusual is happening in a snippet of code (for example, transfer of pointer ownership), leaving textual hints for the reader at the point of use is valuable (std::unique_ptr demonstrates the ownership transfer unambiguously at the call site).
本规范主要是为了代码的阅读,而不是代码的编写
我们期待我们的代码能够重复使用。所以更多的时间用于阅读,而不是编写。所以规范是为了平均水平的软件工程师对代码的阅读、维护和调试的容易,而不是写代码的方便。方便阅读是次要点:当代码中有些特殊做法时,一定要写下文字说明,告诉读者为什么要这么做。
Be consistent with existing code
Using one style consistently through our codebase lets us focus on other (more important) issues. Consistency also allows for automation: tools that format your code or adjust your #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.
与现有的代码保持一致。
与现有的代码风格保持一致,能让我们把精力放在更重要的事情上去。一致性也更有利于自动化:当你的#include语句与自动化工具期待的格式一致时,他才能格式化你的代码。多数情况下,我们需要的是遵守规则,而不是去担心他。所以不要争论,遵守就行了。
Be consistent with the broader C++ community when appropriate
Consistency with the way other organizations use C++ has value for the same reasons as consistency within our code base. If a feature in the C++ standard solves a problem, or if some idiom is widely known and accepted, that's an argument for using it. However, sometimes standard features and idioms are flawed, or were just designed without our codebase's needs in mind. In those cases (as described below) it's appropriate to constrain or ban standard features. In some cases we prefer a homegrown or third-party library over a library defined in the C++ Standard, either out of perceived superiority or insufficient value to transition the codebase to the standard interface.
当适当时,与更大的群体保持一致
与别的用C++作为开发语言的团体保持一致原因也是相同的。如果C++标准的一个特性解决了一个问题,或者一种范式约定俗成并被广泛接受,那就有用他的原因。然而有时标准特性和范式是有缺陷的,或者没有考试代码规模。在这种情况下,禁用这些标准是合适的。在一些情况下,我们倾向于用自己开发的或者第三方的库,而不是C++标准库,或者没有必要为了转化成标准库而修改代码。
Avoid surprising or dangerous constructs
C++ has features that are more surprising or dangerous than one might think at a glance. Some style guide restrictions are in place to prevent falling into these pitfalls. There is a high bar for style guide waivers on such restrictions, because waiving such rules often directly risks compromising program correctness.
避免意外原则
C++有些特性比我们想象更容易带来问题。有些规则就是为了让你不跳进这种火坑。这些规则有较高的优化级,因为如果放弃这些规则开发的代码更容易出问题。
Avoid constructs that our average C++ programmer would find tricky or hard to maintain
C++ has features that may not be generally appropriate because of the complexity they introduce to the code. In widely used code, it may be more acceptable to use trickier language constructs, because any benefits of more complex implementation are multiplied widely by usage, and the cost in understanding the complexity does not need to be paid again when working with new portions of the codebase. When in doubt, waivers to rules of this type can be sought by asking your project leads. This is specifically important for our codebase because code ownership and team membership changes over time: even if everyone that works with some piece of code currently understands it, such understanding is not guaranteed to hold a few years from now.
避免编写一般水平的程序员认为怪异或者难维护的代码
C++有些特性因其复杂而没有被广泛接受。在广泛使用的代码中,一些难懂代码因其使用的广度而代价大增。这些复杂的做法,可能现在还可以理解,但几年以后可能就不会。
Be mindful of our scale
With a codebase of 100+ million lines and thousands of engineers, some mistakes and simplifications for one engineer can become costly for many. For instance it's particularly important to avoid polluting the global namespace: name collisions across a codebase of hundreds of millions of lines are difficult to work with and hard to avoid if everyone puts things into the global namespace.
记住规模
对于上百万行代码和上千名开发者的团队来说,错误会被大大的放大。比如不能污染全局命名空间:如果每个人都把代码放到全局命名空间,对于上百万行代码库是很难处理的。
Concede to optimization when necessary
Performance optimizations can sometimes be necessary and appropriate, even when they conflict with the other principles of this document.
当必要的时候让步于优化
运行优化在某些时候是必须的,即使与本文中的某些堆范相违。
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.
本规范的目的是为了合理的限制提供最大化的指导。通常常识和感觉是正确的。所以根据自己的需要,使用需谨慎。
Currently, code should target C++11, i.e., should not use C++14 or C++17 features. The C++ version targeted by this guide will advance (aggressively) over time.
本规范用于C++ 11。
Code should avoid features that have been removed from the latest language version (currently C++17), as well as the rare cases where code has a different meaning in that latest version. Use of some C++ features is restricted or disallowed. Do not use non-standard extensions.
C++ 17中有些改变。
In general, every .cc file should have an associated .h file. There are some common exceptions, such as unittests and small .cc files containing just a main() function.
Correct use of header files can make a huge difference to the readability, size and performance of your code.
The following rules will guide you through the various pitfalls of using header files.
头文件
一般情况情况下每一个源文件都要一个头文件与之对应,但也有例外。头文件的正确使用能极大的提高可读性、代码大小和执行效率。
头文件自包含
Header files should be self-contained (compile on their own) and end in .h. Non-header files that are meant for inclusion should end in .inc and be used sparingly.
头文件应该自包含的,.inc现在已经很少用了。
All header files should be self-contained. Users and refactoring tools should not have to adhere to special conditions to include the header. Specifically, a header should have header guards and include all other headers it needs.
所有的头文件都应该自包含。用户或者重构工具不需要根据特定的条件包含新的头文件。头文件应该包含保护符引用其他所有的需要的头文件。
Prefer placing the definitions for template and inline functions in the same file as their declarations. The definitions of these constructs must be included into every .cc file that uses them, or the program may fail to link in some build configurations. If declarations and definitions are in different files, including the former should transitively include the latter. Do not move these definitions to separately included header files (-inl.h); this practice was common in the past, but is no longer allowed.
要把模板和内联函数的定义和声名放到同一个文件中。这些定义要被包含到用到他们的源文件中,不然会出现链接错误。如果声明和定义放到了不同的文件,引用一个文件传递性的也引用了另一个。不要放到多个头文件中,这种实践在过去较常用,但现在不允许了。
As an exception, a template that is explicitly instantiated for all relevant sets of template arguments, or that is a private implementation detail of a class, is allowed to be defined in the one and only .cc file that instantiates the template.
There are rare cases where a file designed to be included is not self-contained. These are typically intended to be included at unusual locations, such as the middle of another file. They might not use header guards, and might not include their prerequisites. Name such files with the .inc extension. Use sparingly, and prefer self-contained headers when possible.
All header files should have #define guards to prevent multiple inclusion. The format of the symbol name should be <PROJECT>_<PATH>_<FILE>_H_.
所有的头文件都需要加上保护符,以防止多次被引用。
To guarantee uniqueness, they should be based on the full path in a project's source tree. For example, the file foo/src/bar/baz.h in project foo should have the following guard:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
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.
- Forward declarations can save compile time, as #includes force the compiler to open more files and process more input.
- Forward declarations can save on unnecessary recompilation. #includes can force your code to be recompiled more often, due to unrelated changes in the header.
- Forward declarations can hide a dependency, allowing user code to skip necessary recompilation when headers change.
- A forward declaration may be broken by subsequent changes to the library. Forward declarations of functions and templates can prevent the header owners from making otherwise-compatible changes to their APIs, such as widening a parameter type, adding a template parameter with a default value, or migrating to a new namespace.
- Forward declaring symbols from namespace std:: yields undefined behavior.
- It can be difficult to determine whether 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*).
- Forward declaring multiple symbols from a header can be more verbose than simply #includeing the header.
- Structuring code to enable forward declarations (e.g. using pointer members instead of object members) can make the code slower and more complex.
- Try to avoid forward declarations of entities defined in another project.
- When using a function declared in a header file, always #include that header.
- When using a class template, prefer to #include its header file.
Please see Names and Order of Includes for rules about when to #include a header.
Define functions inline only when they are small, say, 10 lines or fewer.
只有在一个函数少于10行,才声明为内联。
You can declare functions in a way that allows the compiler to expand them inline rather than calling them through the usual function call mechanism.
Inlining a function can generate more efficient object code, as long as the inlined function is small. Feel free to inline accessors and mutators, and other short, performance-critical functions.
Overuse of inlining can actually make programs slower. Depending on a function's size, inlining it can cause the code size to increase or decrease. Inlining a very small accessor function will usually decrease code size while inlining a very large function can dramatically increase code size. On modern processors smaller code usually runs faster due to better use of the instruction cache.
A decent rule of thumb is to not inline a function if it is more than 10 lines long. Beware of destructors, which are often longer than they appear because of implicit member- and base-destructor calls!
Another useful rule of thumb: it's typically not cost effective to inline functions with loops or switch statements (unless, in the common case, the loop or switch statement is never executed).
It is important to know that functions are not always inlined even if they are declared as such; for example, virtual and recursive functions are not normally inlined. Usually recursive functions should not be inline. The main reason for making a virtual function inline is to place its definition in the class, either for convenience or to document its behavior, e.g., for accessors and mutators.
Use standard order for readability and to avoid hidden dependencies: Related header, C library, C++ library, other libraries' .h, your project's .h.
All of a project's header files should be listed as descendants of the project's source directory without use of UNIX directory shortcuts . (the current directory) or .. (the parent directory). For example,google-awesome-project/src/base/logging.h should be included as:
#include "base/logging.h"
In dir/foo.cc or dir/foo_test.cc, whose main purpose is to implement or test the stuff in dir2/foo2.h, order your includes as follows:
- dir2/foo2.h.
- A blank line
- C system files.
- C++ system files.
- A blank line
- Other libraries' .h files.
- Your project's .h files.
Note that any adjacent blank lines should be collapsed.
With the preferred ordering, if dir2/foo2.h omits any necessary includes, the build of dir/foo.cc or dir/foo_test.cc will break. Thus, this rule ensures that build breaks show up first for the people working on these files, not for innocent people in other packages.
dir/foo.cc and dir2/foo2.h are usually in the same directory (e.g. base/basictypes_test.cc and base/basictypes.h), but may sometimes be in different directories too.
Note that the C compatibility headers such as stddef.h are essentially interchangeable with their C++ counterparts (cstddef) Either style is acceptable, but prefer consistency with existing code.
Within each section the includes should be ordered alphabetically. Note that older code might not conform to this rule and should be fixed when convenient.
You should include all the headers that define the symbols you rely 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.hwhich (currently) includes bar.h: include bar.h yourself, unless foo.h explicitly demonstrates its intent to provide you the symbols of bar.h. However, any includes present in the related header do not need to be included again in the related cc (i.e., foo.cc can rely on foo.h's includes).
For example, the includes in google-awesome-project/src/foo/internal/fooserver.cc might look like this:
#include "foo/server/fooserver.h"
#include <sys/types.h>
#include <unistd.h>
#include <vector>
#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/server/bar.h"
Sometimes, system-specific code needs conditional includes. Such code can put conditional includes after other includes. Of course, keep your system-specific code small and localized. Example:
#include "foo/public/fooserver.h"
#include "base/port.h" // For LANG_CXX11.
#ifdef LANG_CXX11
#include <initializer_list>
#endif // LANG_CXX11
With few exceptions, place code in a namespace. Namespaces should have unique names based on the project name, and possibly its path. Do not use using-directives (e.g. using namespace foo). Do not use inline namespaces. For unnamed namespaces, see Unnamed Namespaces and Static Variables.
Namespaces subdivide the global scope into distinct, named scopes, and so are useful for preventing name collisions in the global scope.
Namespaces provide a method for preventing name conflicts in large programs while allowing most code to use reasonably 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, 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 example:
namespace outer {
inline namespace inner {
void foo();
} // namespace inner
} // namespace outer
The expressions outer::inner::foo() and outer::foo() are interchangeable. Inline namespaces are primarily intended for ABI compatibility across versions.
Namespaces can be confusing, because they complicate the mechanics of figuring out what definition a name refers to.
Inline namespaces, in particular, can be confusing because names aren't actually restricted to the namespace where they are declared. They are only useful as part of some larger versioning policy.
In some contexts, it's necessary to repeatedly refer to symbols by their fully-qualified names. For deeply-nested namespaces, this can add a lot of clutter.
Namespaces should be used as follows:
- Follow the rules on Namespace Names.
- Terminate namespaces with comments as shown in the given examples.
- Namespaces wrap the entire source file after includes, gflags definitions/declarations and forward declarations of classes from other namespaces.
- // In the .h file
- namespace mynamespace {
- // All declarations are within the namespace scope.
- // Notice the lack of indentation.
- class MyClass {
- public:
- ...
- void Foo();
- };
- } // namespace mynamespace
- // In the .cc file
- namespace mynamespace {
- // Definition of functions is within scope of the namespace.
- void MyClass::Foo() {
- ...
- }
- } // namespace mynamespace
More complex .cc files might have additional details, like flags or using-declarations.
#include "a.h"
DEFINE_FLAG(bool, someflag, false, "dummy flag");
namespace mynamespace {
using ::foo::bar;
...code for mynamespace... // Code goes against the left margin.
} // namespace mynamespace
- To place generated protocol message code in a namespace, use the package specifier in the .proto file. See Protocol Buffer Packages for details.
- Do not declare anything in namespace 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 library, include the appropriate header file.
- You may not use a using-directive to make all names from a namespace available.
- // Forbidden -- This pollutes the namespace.
- using namespace foo;
- Do not use Namespace aliases at namespace scope in header files except in explicitly marked internal-only namespaces, because anything imported into a namespace in a header file becomes part of the public API exported by that file.
- // Shorten access to some commonly used names in .cc files.
- namespace baz = ::foo::bar::baz;
- // Shorten access to some commonly used names (in a .h file).
- namespace librarian {
- 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 baz = ::foo::bar::baz;
- ...
- }
- } // namespace librarian
- Do not use inline namespaces.
Unnamed Namespaces and Static Variables
When definitions in a .cc file do not need to be referenced outside that file, place them in an unnamed namespace or declare them static. Do not use either of these constructs in .h files.
All declarations can be given internal linkage by placing them in unnamed namespaces. Functions and variables can also be given internal linkage by declaring them static. This means that anything you're declaring can't be accessed from another file. If a different file declares something with the same name, then the two entities are completely independent.
Use of internal linkage in .cc files is encouraged for all code that does not need to be referenced elsewhere. Do not use internal linkage in .h files.
Format unnamed namespaces like named namespaces. In the terminating comment, leave the namespace name empty:
namespace {
...
} // namespace
Nonmember, Static Member, and Global Functions
Prefer placing nonmember functions in a namespace; use completely global functions rarely. Do not use a class simply to group static functions. Static methods of a class should generally be closely related to instances of the class or the class's static data.
Nonmember and static member functions can be useful in some situations. Putting nonmember functions in a namespace avoids polluting the global namespace.
Nonmember and static member functions may make more sense as members of a new class, especially if they access external resources or have significant dependencies.
Sometimes it is useful to define a function not bound to a class instance. Such a function can be either a static member or a nonmember function. Nonmember functions should not depend on external variables, and should nearly always exist in a namespace. Do not create classes only to group static member functions; this is no different than just giving the function names a common prefix, and such grouping is usually unnecessary anyway.
If you define a nonmember function and it is only needed in its .cc file, use internal linkage to limit its scope.