Update C++ style guide.

- Reword guidance for definitions of inline functions and templates.
- Capitalize the first letter of various comments.
- Update command-line flag reference to use absl flags.
- Improve braced-initialization example for 64-bit constants.
- Delete C++03 guidance for NULL.
- Clarify wording around wrapping of string literals.
- Use C++17 [[fallthrough]] instead of ABSL_FALLTHROUGH.
This commit is contained in:
Daniel Cheng 2022-05-03 23:57:01 +00:00
parent 2eb7e16647
commit 93c7bd88c6

View File

@ -204,19 +204,15 @@ header. Specifically, a header should
have <a href="#The__define_Guard">header guards</a> and include all have <a href="#The__define_Guard">header guards</a> and include all
other headers it needs.</p> other headers it needs.</p>
<p>Prefer placing the definitions for template and inline functions in <p>When a header declares inline functions or templates that clients of the
the same file as their declarations. The definitions of these header will instantiate, the inline functions and templates must also have
constructs must be included into every <code>.cc</code> file that uses definitions in the header, either directly or in files it includes. Do not move
them, or the program may fail to link in some build configurations. If these definitions to separately included header (<code>-inl.h</code>) files;
declarations and definitions are in different files, including the this practice was common in the past, but is no longer allowed. When all
former should transitively include the latter. Do not move these instantiations of a template occur in one <code>.cc</code> file, either because
definitions to separately included header files (<code>-inl.h</code>); they're <a href="https://en.cppreference.com/w/cpp/language/class_template#Explicit_instantiation">
this practice was common in the past, but is no longer allowed.</p> explicit</a> or because the definition is accessible to only
the <code>.cc</code> file, the template definition can be kept in that file.</p>
<p>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 <code>.cc</code> file that instantiates the template.</p>
<p>There are rare cases where a file designed to be included is not <p>There are rare cases where a file designed to be included is not
self-contained. These are typically intended to be included at unusual self-contained. These are typically intended to be included at unusual
@ -331,7 +327,7 @@ struct D : B {};
#include "b.h" #include "b.h"
void f(B*); void f(B*);
void f(void*); void f(void*);
void test(D* x) { f(x); } // calls f(B*) void test(D* x) { f(x); } // Calls f(B*)
</pre> </pre>
If the <code>#include</code> was replaced with forward If the <code>#include</code> was replaced with forward
decls for <code>B</code> and <code>D</code>, decls for <code>B</code> and <code>D</code>,
@ -492,8 +488,8 @@ might look like this:</p>
#include &lt;vector&gt; #include &lt;vector&gt;
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/server/bar.h" #include "foo/server/bar.h"
#include "third_party/absl/flags/flag.h"
</pre> </pre>
<p><b>Exception:</b></p> <p><b>Exception:</b></p>
@ -618,7 +614,7 @@ void MyClass::Foo() {
<pre>#include "a.h" <pre>#include "a.h"
ABSL_FLAG(bool, someflag, false, "dummy flag"); ABSL_FLAG(bool, someflag, false, "a flag");
namespace mynamespace { namespace mynamespace {
@ -869,26 +865,26 @@ objects with static storage duration if they are trivially destructible.
Fundamental types (like pointers and <code>int</code>) are trivially Fundamental types (like pointers and <code>int</code>) are trivially
destructible, as are arrays of trivially destructible types. Note that destructible, as are arrays of trivially destructible types. Note that
variables marked with <code>constexpr</code> are trivially destructible.</p> variables marked with <code>constexpr</code> are trivially destructible.</p>
<pre>const int kNum = 10; // allowed <pre>const int kNum = 10; // Allowed
struct X { int n; }; struct X { int n; };
const X kX[] = {{1}, {2}, {3}}; // allowed const X kX[] = {{1}, {2}, {3}}; // Allowed
void foo() { void foo() {
static const char* const kMessages[] = {"hello", "world"}; // allowed static const char* const kMessages[] = {"hello", "world"}; // Allowed
} }
// allowed: constexpr guarantees trivial destructor // Allowed: constexpr guarantees trivial destructor.
constexpr std::array&lt;int, 3&gt; kArray = {{1, 2, 3}};</pre> constexpr std::array&lt;int, 3&gt; kArray = {1, 2, 3};</pre>
<pre class="badcode">// bad: non-trivial destructor <pre class="badcode">// bad: non-trivial destructor
const std::string kFoo = "foo"; const std::string kFoo = "foo";
// bad for the same reason, even though kBar is a reference (the // Bad for the same reason, even though kBar is a reference (the
// rule also applies to lifetime-extended temporary objects) // rule also applies to lifetime-extended temporary objects).
const std::string&amp; kBar = StrCat("a", "b", "c"); const std::string&amp; kBar = StrCat("a", "b", "c");
void bar() { void bar() {
// bad: non-trivial destructor // Bad: non-trivial destructor.
static std::map&lt;int, int&gt; kData = {{1, 0}, {2, 0}, {3, 0}}; static std::map&lt;int, int&gt; kData = {{1, 0}, {2, 0}, {3, 0}};
}</pre> }</pre>
@ -902,10 +898,10 @@ applies, though. In particular, a function-local static reference of the form
<p>Initialization is a more complex topic. This is because we must not only <p>Initialization is a more complex topic. This is because we must not only
consider whether class constructors execute, but we must also consider the consider whether class constructors execute, but we must also consider the
evaluation of the initializer:</p> evaluation of the initializer:</p>
<pre class="neutralcode">int n = 5; // fine <pre class="neutralcode">int n = 5; // Fine
int m = f(); // ? (depends on f) int m = f(); // ? (Depends on f)
Foo x; // ? (depends on Foo::Foo) Foo x; // ? (Depends on Foo::Foo)
Bar y = g(); // ? (depends on g and on Bar::Bar) Bar y = g(); // ? (Depends on g and on Bar::Bar)
</pre> </pre>
<p>All but the first statement expose us to indeterminate initialization <p>All but the first statement expose us to indeterminate initialization
@ -918,9 +914,9 @@ constructor call, then the constructor must be specified as
<code>constexpr</code>, too:</p> <code>constexpr</code>, too:</p>
<pre>struct Foo { constexpr Foo(int) {} }; <pre>struct Foo { constexpr Foo(int) {} };
int n = 5; // fine, 5 is a constant expression int n = 5; // Fine, 5 is a constant expression.
Foo x(2); // fine, 2 is a constant expression and the chosen constructor is constexpr Foo x(2); // Fine, 2 is a constant expression and the chosen constructor is constexpr.
Foo a[] = { Foo(1), Foo(2), Foo(3) }; // fine</pre> Foo a[] = { Foo(1), Foo(2), Foo(3) }; // Fine</pre>
<p>Constant initialization is always allowed. Constant initialization of <p>Constant initialization is always allowed. Constant initialization of
static storage duration variables should be marked with <code>constexpr</code> static storage duration variables should be marked with <code>constexpr</code>
@ -936,22 +932,22 @@ dynamic initialization, and reviewed very carefully.</p>
<p>By contrast, the following initializations are problematic:</p> <p>By contrast, the following initializations are problematic:</p>
<pre class="badcode">// Some declarations used below. <pre class="badcode">// Some declarations used below.
time_t time(time_t*); // not constexpr! time_t time(time_t*); // Not constexpr!
int f(); // not constexpr! int f(); // Not constexpr!
struct Bar { Bar() {} }; struct Bar { Bar() {} };
// Problematic initializations. // Problematic initializations.
time_t m = time(nullptr); // initializing expression not a constant expression time_t m = time(nullptr); // Initializing expression not a constant expression.
Foo y(f()); // ditto Foo y(f()); // Ditto
Bar b; // chosen constructor Bar::Bar() not constexpr</pre> Bar b; // Chosen constructor Bar::Bar() not constexpr.</pre>
<p>Dynamic initialization of nonlocal variables is discouraged, and in general <p>Dynamic initialization of nonlocal variables is discouraged, and in general
it is forbidden. However, we do permit it if no aspect of the program depends it is forbidden. However, we do permit it if no aspect of the program depends
on the sequencing of this initialization with respect to all other on the sequencing of this initialization with respect to all other
initializations. Under those restrictions, the ordering of the initialization initializations. Under those restrictions, the ordering of the initialization
does not make an observable difference. For example:</p> does not make an observable difference. For example:</p>
<pre>int p = getpid(); // allowed, as long as no other static variable <pre>int p = getpid(); // Allowed, as long as no other static variable
// uses p in its own initialization</pre> // uses p in its own initialization.</pre>
<p>Dynamic initialization of static local variables is allowed (and common).</p> <p>Dynamic initialization of static local variables is allowed (and common).</p>
@ -969,19 +965,20 @@ does not make an observable difference. For example:</p>
<li>Maps, sets, and other dynamic containers: if you require a static, fixed <li>Maps, sets, and other dynamic containers: if you require a static, fixed
collection, such as a set to search against or a lookup table, you cannot collection, such as a set to search against or a lookup table, you cannot
use the dynamic containers from the standard library as a static variable, use the dynamic containers from the standard library as a static variable,
since they have non-trivial destructors. Instead, consider a simple array of since they have non-trivial destructors. Instead, consider
trivial types, e.g., an array of arrays of ints (for a "map from int to
a simple array of trivial types, e.g., an array of arrays of ints (for a "map from int to
int"), or an array of pairs (e.g., pairs of <code>int</code> and <code>const int"), or an array of pairs (e.g., pairs of <code>int</code> and <code>const
char*</code>). For small collections, linear search is entirely sufficient char*</code>). For small collections, linear search is entirely sufficient
(and efficient, due to memory locality); consider using the facilities from (and efficient, due to memory locality); consider using the facilities from
<a href="https://github.com/abseil/abseil-cpp/blob/master/absl/algorithm/container.h">absl/algorithm/container.h</a> <a href="https://github.com/abseil/abseil-cpp/blob/master/absl/algorithm/container.h">absl/algorithm/container.h</a>
for the standard operations. If necessary, keep the collection in sorted for the standard operations. If necessary, keep the collection in sorted
order and use a binary search algorithm. If you do really prefer a dynamic order and use a binary search algorithm.
container from the standard library, consider using a function-local static
pointer, as described below.</li> If you do really prefer a dynamic container from the standard library, consider using
a function-local static pointer, as described below
.</li>
<li>Smart pointers (<code>unique_ptr</code>, <code>shared_ptr</code>): smart <li>Smart pointers (<code>unique_ptr</code>, <code>shared_ptr</code>): smart
pointers execute cleanup during destruction and are therefore forbidden. pointers execute cleanup during destruction and are therefore forbidden.
Consider whether your use case fits into one of the other patterns described Consider whether your use case fits into one of the other patterns described
@ -991,8 +988,8 @@ does not make an observable difference. For example:</p>
a type that you need to define yourself, give the type a trivial destructor a type that you need to define yourself, give the type a trivial destructor
and a <code>constexpr</code> constructor.</li> and a <code>constexpr</code> constructor.</li>
<li>If all else fails, you can create an object dynamically and never delete <li>If all else fails, you can create an object dynamically and never delete
it by using a function-local static pointer or reference (e.g., <code>static it by using a function-local static pointer or reference (e.g.,
const auto&amp; impl = *new T(args...);</code>).</li> <code>static const auto&amp; impl = *new T(args...);</code>).</li>
</ul> </ul>
<h3 id="thread_local">thread_local Variables</h3> <h3 id="thread_local">thread_local Variables</h3>
@ -1705,11 +1702,27 @@ sections that would be empty.</p>
<p>Within each section, prefer grouping similar <p>Within each section, prefer grouping similar
kinds of declarations together, and prefer the kinds of declarations together, and prefer the
following order: types and type aliases (<code>typedef</code>, following order:</p>
<code>using</code>, <code>enum</code>, nested structs and classes),
static constants, factory functions, constructors and assignment <ol>
operators, destructor, all other member and <code>friend</code> functions, <li>Types and type aliases (<code>typedef</code>, <code>using</code>,
data members.</p> <code>enum</code>, nested structs and classes)</li>
<li>Static constants</li>
<li>Factory functions</li>
<li>Constructors and assignment operators</li>
<li>Destructor</li>
<li>
All other functions (<code>static</code> and non-<code>static</code> member
functions, and <code>friend</code> functions)
</li>
<li>Data members (static and non-static)</li>
</ol>
<p>Do not put large method definitions inline in the <p>Do not put large method definitions inline in the
class definition. Usually, only trivial or class definition. Usually, only trivial or
@ -2998,7 +3011,7 @@ problems of printing, comparisons, and structure alignment.</p>
<p>Use <a href="#Casting">braced-initialization</a> as needed to create <p>Use <a href="#Casting">braced-initialization</a> as needed to create
64-bit constants. For example:</p> 64-bit constants. For example:</p>
<pre>int64_t my_value{0x123456789}; <pre>int64_t my_value{0x123456789};
uint64_t my_mask{3ULL &lt;&lt; 48}; uint64_t my_mask{uint64_t{3} &lt;&lt; 48};
</pre> </pre>
</li> </li>
</ul> </ul>
@ -3077,6 +3090,8 @@ possible:</p>
<li>Prefer not using <code>##</code> to generate <li>Prefer not using <code>##</code> to generate
function/class/variable names.</li> function/class/variable names.</li>
</ul> </ul>
<p>Exporting macros from headers (i.e., defining them in a header <p>Exporting macros from headers (i.e., defining them in a header
@ -3094,12 +3109,6 @@ not the <code>0</code> literal).</p>
<p>For pointers (address values), use <code>nullptr</code>, as this <p>For pointers (address values), use <code>nullptr</code>, as this
provides type-safety.</p> provides type-safety.</p>
<p>For C++03 projects, prefer <code>NULL</code> to <code>0</code>. While the
values are equivalent, <code>NULL</code> looks more like a pointer to the
reader, and some C++ compilers provide special definitions of <code>NULL</code>
which enable them to give useful warnings. Never use <code>NULL</code> for
numeric (integer or floating-point) values.</p>
<p>Use <code>'\0'</code> for the null character. Using the correct type makes <p>Use <code>'\0'</code> for the null character. Using the correct type makes
the code more readable.</p> the code more readable.</p>
@ -4759,8 +4768,20 @@ can easily show longer lines.</p>
readability, ease of cut and paste or auto-linking -- e.g., if a line 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.</li> contains an example command or a literal URL longer than 80 characters.</li>
<li>a raw-string literal with content that exceeds 80 characters. Except for <li>a string literal that cannot easily be wrapped at 80 columns.
test code, such literals should appear near the top of a file.</li> This may be because it contains URIs or other semantically-critical pieces,
or because the literal contains an embedded language, or a multiline
literal whose newlines are significant like help messages.
In these cases, breaking up the literal would
reduce readability, searchability, ability to click links, etc. Except for
test code, such literals should appear at namespace scope near the top of a
file. If a tool like Clang-Format doesn't recognize the unsplittable content,
<a href="https://clang.llvm.org/docs/ClangFormatStyleOptions.html#disabling-formatting-on-a-piece-of-code">
disable the tool</a> around the content as necessary.
<br><br>
(We must balance between usability/searchability of such literals and the
readability of the code around them.)
</li>
<li>an include statement.</li> <li>an include statement.</li>
@ -5200,10 +5221,8 @@ case should never execute, treat this as an error. For example:
<p>Fall-through from one case label to <p>Fall-through from one case label to
another must be annotated using the another must be annotated using the
<code>ABSL_FALLTHROUGH_INTENDED;</code> macro (defined in <code>[[fallthrough]];</code> attribute.
<code>[[fallthrough]];</code> should be placed at a
<code>absl/base/macros.h</code>).
<code>ABSL_FALLTHROUGH_INTENDED;</code> should be placed at a
point of execution where a fall-through to the next case point of execution where a fall-through to the next case
label occurs. A common exception is consecutive case label occurs. A common exception is consecutive case
labels without intervening code, in which case no labels without intervening code, in which case no
@ -5214,14 +5233,14 @@ annotation is needed.</p>
case 43: case 43:
if (dont_be_picky) { if (dont_be_picky) {
// Use this instead of or along with annotations in comments. // Use this instead of or along with annotations in comments.
ABSL_FALLTHROUGH_INTENDED; [[fallthrough]];
} else { } else {
CloseButNoCigar(); CloseButNoCigar();
break; break;
} }
case 42: case 42:
DoSomethingSpecial(); DoSomethingSpecial();
ABSL_FALLTHROUGH_INTENDED; [[fallthrough]];
default: default:
DoSomethingGeneric(); DoSomethingGeneric();
break; break;