From 93c7bd88c65a897b71a4afd77a305b3420ce5d98 Mon Sep 17 00:00:00 2001
From: Daniel Cheng
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.
When a header declares inline functions or templates that clients of the
+header will instantiate, the inline functions and templates must also have
+definitions in the header, either directly or in files it includes. Do not move
+these definitions to separately included header (-inl.h
) files;
+this practice was common in the past, but is no longer allowed. When all
+instantiations of a template occur in one .cc
file, either because
+they're
+explicit or because the definition is accessible to only
+the .cc
file, the template definition can be kept in that file.
There are rare cases where a file designed to be included is not
self-contained. These are typically intended to be included at unusual
@@ -331,7 +327,7 @@ struct D : B {};
#include "b.h"
void f(B*);
void f(void*);
-void test(D* x) { f(x); } // calls f(B*)
+void test(D* x) { f(x); } // Calls f(B*)
If the #include
was replaced with forward
decls for B
and D
,
@@ -492,8 +488,8 @@ might look like this:
Exception:
@@ -618,7 +614,7 @@ void MyClass::Foo() {#include "a.h" -ABSL_FLAG(bool, someflag, false, "dummy flag"); +ABSL_FLAG(bool, someflag, false, "a flag"); namespace mynamespace { @@ -869,26 +865,26 @@ objects with static storage duration if they are trivially destructible. Fundamental types (like pointers andint
) are trivially destructible, as are arrays of trivially destructible types. Note that variables marked withconstexpr
are trivially destructible. -const int kNum = 10; // allowed +const int kNum = 10; // Allowed struct X { int n; }; -const X kX[] = {{1}, {2}, {3}}; // allowed +const X kX[] = {{1}, {2}, {3}}; // Allowed void foo() { - static const char* const kMessages[] = {"hello", "world"}; // allowed + static const char* const kMessages[] = {"hello", "world"}; // Allowed } -// allowed: constexpr guarantees trivial destructor -constexpr std::array<int, 3> kArray = {{1, 2, 3}};+// Allowed: constexpr guarantees trivial destructor. +constexpr std::array<int, 3> kArray = {1, 2, 3};// bad: non-trivial destructor const std::string kFoo = "foo"; -// bad for the same reason, even though kBar is a reference (the -// rule also applies to lifetime-extended temporary objects) +// Bad for the same reason, even though kBar is a reference (the +// rule also applies to lifetime-extended temporary objects). const std::string& kBar = StrCat("a", "b", "c"); void bar() { - // bad: non-trivial destructor + // Bad: non-trivial destructor. static std::map<int, int> kData = {{1, 0}, {2, 0}, {3, 0}}; }@@ -902,10 +898,10 @@ applies, though. In particular, a function-local static reference of the formInitialization is a more complex topic. This is because we must not only consider whether class constructors execute, but we must also consider the evaluation of the initializer:
-int n = 5; // fine -int m = f(); // ? (depends on f) -Foo x; // ? (depends on Foo::Foo) -Bar y = g(); // ? (depends on g and on Bar::Bar) +int n = 5; // Fine +int m = f(); // ? (Depends on f) +Foo x; // ? (Depends on Foo::Foo) +Bar y = g(); // ? (Depends on g and on Bar::Bar)All but the first statement expose us to indeterminate initialization @@ -918,9 +914,9 @@ constructor call, then the constructor must be specified as
constexpr
, too:struct Foo { constexpr Foo(int) {} }; -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 a[] = { Foo(1), Foo(2), Foo(3) }; // fine+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 a[] = { Foo(1), Foo(2), Foo(3) }; // FineConstant initialization is always allowed. Constant initialization of static storage duration variables should be marked with
constexpr
@@ -936,22 +932,22 @@ dynamic initialization, and reviewed very carefully.By contrast, the following initializations are problematic:
// Some declarations used below. -time_t time(time_t*); // not constexpr! -int f(); // not constexpr! +time_t time(time_t*); // Not constexpr! +int f(); // Not constexpr! struct Bar { Bar() {} }; // Problematic initializations. -time_t m = time(nullptr); // initializing expression not a constant expression -Foo y(f()); // ditto -Bar b; // chosen constructor Bar::Bar() not constexpr+time_t m = time(nullptr); // Initializing expression not a constant expression. +Foo y(f()); // Ditto +Bar b; // Chosen constructor Bar::Bar() not constexpr.
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 on the sequencing of this initialization with respect to all other initializations. Under those restrictions, the ordering of the initialization does not make an observable difference. For example:
-int p = getpid(); // allowed, as long as no other static variable - // uses p in its own initialization+
int p = getpid(); // Allowed, as long as no other static variable + // uses p in its own initialization.
Dynamic initialization of static local variables is allowed (and common).
@@ -969,19 +965,20 @@ does not make an observable difference. For example:int
and const
char*
). For small collections, linear search is entirely sufficient
(and efficient, due to memory locality); consider using the facilities from
-
absl/algorithm/container.h
-
-
for the standard operations. If necessary, keep the collection in sorted
- order and use a binary search algorithm. If you do really prefer a dynamic
- container from the standard library, consider using a function-local static
- pointer, as described below.unique_ptr
, shared_ptr
): smart
pointers execute cleanup during destruction and are therefore forbidden.
Consider whether your use case fits into one of the other patterns described
@@ -991,8 +988,8 @@ does not make an observable difference. For example:
a type that you need to define yourself, give the type a trivial destructor
and a constexpr
constructor.static
- const auto& impl = *new T(args...);
).static const auto& impl = *new T(args...);
).
Within each section, prefer grouping similar
kinds of declarations together, and prefer the
-following order: types and type aliases (typedef
,
-using
, enum
, nested structs and classes),
-static constants, factory functions, constructors and assignment
-operators, destructor, all other member and friend
functions,
-data members.
typedef
, using
,
+ enum
, nested structs and classes)static
and non-static
member
+ functions, and friend
functions)
+ Do not put large method definitions inline in the class definition. Usually, only trivial or @@ -2998,7 +3011,7 @@ problems of printing, comparisons, and structure alignment.
Use braced-initialization as needed to create 64-bit constants. For example:
int64_t my_value{0x123456789}; -uint64_t my_mask{3ULL << 48}; +uint64_t my_mask{uint64_t{3} << 48};@@ -3077,6 +3090,8 @@ possible:
##
to generate
function/class/variable names.Exporting macros from headers (i.e., defining them in a header
@@ -3094,12 +3109,6 @@ not the 0
literal).
For pointers (address values), use nullptr
, as this
provides type-safety.
For C++03 projects, prefer NULL
to 0
. While the
-values are equivalent, NULL
looks more like a pointer to the
-reader, and some C++ compilers provide special definitions of NULL
-which enable them to give useful warnings. Never use NULL
for
-numeric (integer or floating-point) values.
Use '\0'
for the null character. Using the correct type makes
the code more readable.
auto [/*field_name1=*/ bound_name1, /*field_name2=*/ bound_name2] = ...+
auto [/*field_name1=*/bound_name1, /*field_name2=*/bound_name2] = ...
As with function parameter comments, this can enable tools to detect if you get the order of the fields wrong.
@@ -4759,8 +4768,20 @@ can easily show longer lines. 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. -Fall-through from one case label to
another must be annotated using the
-ABSL_FALLTHROUGH_INTENDED;
macro (defined in
-
-absl/base/macros.h
).
-ABSL_FALLTHROUGH_INTENDED;
should be placed at a
+[[fallthrough]];
attribute.
+[[fallthrough]];
should be placed at a
point of execution where a fall-through to the next case
label occurs. A common exception is consecutive case
labels without intervening code, in which case no
@@ -5214,14 +5233,14 @@ annotation is needed.