From 901474aa08da04b31510a6ba9bdef15c2f99a28e Mon Sep 17 00:00:00 2001
From: Daniel Cheng
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 +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.
@@ -164,9 +164,8 @@ input.Currently, code should target C++17, i.e., should not use C++2x - features, with the exception of designated - initializers. The C++ version targeted by this guide will advance +
Currently, code should target C++20, i.e., should not use C++23 + features. The C++ version targeted by this guide will advance (aggressively) over time.
@@ -176,7 +175,7 @@ input.Consider portability to other environments before using features -from C++14 and C++17 in your project.
+from C++17 and C++20 in your project.Single-line nested namespace declarations + + are preferred in new code, but are not required.
+Constant initialization is always allowed. Constant initialization of
static storage duration variables should be marked with constexpr
-or where possible the
-
-
-
-ABSL_CONST_INIT
-attribute. Any non-local static storage
+or constinit
By contrast, the following initializations are problematic:
@@ -1466,9 +1466,9 @@ break those invariants. Constructors, destructors, and helper methods may be present; however, these methods must not require or enforce any invariants. -If more functionality or invariants are required, a
-class
is more appropriate. If in doubt, make
-it a class
.
If more functionality or invariants are required, or struct has wide visibility and expected to
+evolve, then a class
is more appropriate. If in doubt, make it a class
.
+
For consistency with STL, you can use
struct
instead of class
for
@@ -1678,17 +1678,23 @@ 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.
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.
a + b
compiles but
+b + a
doesn't.
+
+For a type T
whose values can be compared for
+equality, define a non-member operator==
and document when
+two values of type T
are considered equal.
+If there is a single obvious notion of when a value t1
+of type T
is less than another such value t2
then
+you may also define operator<=>
, which should be
+consistent with operator==
.
+Prefer not to overload the other comparison and ordering operators.
Don't go out of your way to avoid defining operator
overloads. For example, prefer to define ==
,
@@ -2874,10 +2880,13 @@ putting the "adjective" (const
) before the
const
first, we do not require it. But be
consistent with the code around you!
Use constexpr
to define true
-constants or to ensure constant initialization.
constinit
to ensure constant
+initialization for non-constant variables.
+
Some variables can be declared constexpr
@@ -2885,7 +2894,8 @@ to indicate the variables are true constants, i.e., fixed at
compilation/link time. Some functions and constructors
can be declared constexpr
which enables them
to be used in defining a constexpr
-variable.
consteval
+to restrict their use to compile time.
Use of constexpr
enables definition of
@@ -2906,9 +2916,11 @@ in these definitions.
constexpr
to specify true
constants and the functions that support their
-definitions. Avoid complexifying function definitions to
+definitions. consteval
may be used for
+code that must not be invoked at runtime.
+Avoid complexifying function definitions to
enable their use with constexpr
. Do not use
-constexpr
to force inlining.
+constexpr
or consteval
to force inlining.
int16_t
, uint32_t
,
int64_t
, etc. You should always use
those in preference to short
, unsigned
long long
and the like, when you need a guarantee
-on the size of an integer. Of the built-in integer types, only
+on the size of an integer. Prefer to omit the std::
+prefix for these types, as the extra 5 characters do
+not merit the added clutter. Of the built-in integer types, only
int
should be used. When appropriate, you
are welcome to use standard type aliases like
size_t
and ptrdiff_t
.
@@ -3148,7 +3162,6 @@ possible:
##
to generate
function/class/variable names.Exporting macros from headers (i.e., defining them in a header
@@ -3525,8 +3538,8 @@ ordering of fields than the Point
example above.
While designated initializers have long been part of the C standard and -supported by C++ compilers as an extension, only recently have they made it -into the C++ standard, being added as part of C++20.
+supported by C++ compilers as an extension, they were not supported by +C++ prior to C++20.The rules in the C++ standard are stricter than in C and compiler extensions, requiring that the designated initializers appear in the same order as the @@ -3768,6 +3781,113 @@ error messages are part of your user interface, and your code should be tweaked as necessary so that the error messages are understandable and actionable from a user point of view.
+Use concepts sparingly.
+In general, concepts and constraints should only be used in cases
+where templates would have been used prior to C++20.
+Avoid introducing new concepts in headers,
+unless the headers are marked as internal to the library.
+Do not define concepts that are not enforced by the compiler.
+Prefer constraints over template metaprogramming, and
+avoid the template<Concept T>
syntax;
+instead, use the requires(Concept<T>)
+syntax.
The concept
keyword is a new mechanism for defining
+requirements (such as type traits or interface specifications)
+for a template parameter.
+The requires
keyword provides mechanisms for placing
+anonymous constraints on templates and verifying that constraints
+are satisfied at compile time.
+Concepts and constraints are often used together, but can be
+also used independently.
Predefined concepts in the standard library should be
+preferred to type traits, when equivalent ones exist.
+(e.g., if std::is_integral_v
would have been used
+before C++20, then std::integral
should be used in
+C++20 code.)
+Similarly, prefer modern constraint syntax
+(via requires(Condition)
).
+Avoid legacy template metaprogramming constructs
+(such as std::enable_if<Condition>
)
+as well as the template<Concept T>
+syntax.
Do not manually re-implement any existing concepts or traits.
+For example, use
+requires(std::default_initializable<T>)
+instead of
+requires(requires { T v; })
+or the like.
+
+
New concept
declarations should be rare, and only
+defined internally within a library, such that they are not
+exposed at API boundaries.
+More generally, do not use concepts or constraints in cases where
+you wouldn't use their legacy template equivalents in C++17.
+
Do not define concepts that duplicate the function body, +or impose requirements that would be insignificant or obvious +from reading the body of the code or the resulting error messages. +For example, avoid the following: +
template <typename T> // Bad - redundant with negligible benefit +concept Addable = std::copyable<T> && requires(T a, T b) { a + b; }; +template <Addable T> +T Add(T x, T y, T z) { return x + y + z; } ++Instead, prefer to leave code as an ordinary template unless +you can demonstrate that concepts result in significant +improvement for that particular case, such as in the resulting +error messages for a deeply nested or non-obvious +requirement. + +
Concepts should be statically verifiable by the compiler. +Do not use any concept whose primary benefits would come from a +semantic (or otherwise unenforced) constraint. +Requirements that are unenforced at compile time should instead +be imposed via other mechanisms such as comments, assertions, +or tests.
+Use only approved libraries from the Boost library @@ -3955,9 +4075,10 @@ guide, the following C++ features may not be used:
There are several ways to create names that are aliases of other entities:
-typedef Foo Bar; -using Bar = Foo; -using other_namespace::Foo; +using Bar = Foo; +typedef Foo Bar; // But prefer `using` in C++ code. +using ::other_namespace::Foo; +using enum MyEnumType; // Creates aliases for all enumerators in MyEnumType.In new code,
using
is preferable totypedef
, @@ -4308,9 +4429,10 @@ const int kAndroid8_0_0 = 24; // Android 8.0.0All 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. For example:
+Storage Duration for details) should be named this way, including those in templates where +different instantiations of the template may have different values. This convention is optional for +variables of other storage classes, e.g., automatic variables; otherwise the usual variable naming +rules apply. For example:void ComputeFoo(absl::string_view suffix) { // Either of these is acceptable. @@ -4515,7 +4637,7 @@ author line, consider deleting the author line. New files should usually not contain copyright notice or author line. -+Class Comments
+Struct and Class Comments
Every non-obvious class or struct declaration should have an accompanying comment that describes what it is for and how it should @@ -4532,6 +4654,8 @@ class GargantuanTableIterator { };
Class Comments
+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 @@ -4925,7 +5049,8 @@ if included in the source as straight UTF-8.
When possible, avoid the
u8
prefix. It has significantly different semantics starting in C++20 than in C++17, producing arrays ofchar8_t
-rather thanchar
. +rather thanchar
, and will change again in C++23. +You shouldn't use
char16_t
andchar32_t
character types, since they're for