diff --git a/cppguide.html b/cppguide.html index e8e308c..59d3682 100644 --- a/cppguide.html +++ b/cppguide.html @@ -165,7 +165,8 @@ input.
Currently, code should target C++17, i.e., should not use C++2x - features. The C++ version targeted by this guide will advance + features, with the exception of designated + initializers. The C++ version targeted by this guide will advance (aggressively) over time.
@@ -441,6 +442,8 @@ as follows:.h
files.constexpr
variable of
+ string_view
, character array, or character pointer, pointing
+ to a string literal. String literals have static storage duration already
+ and are usually sufficient.
+ See TotW #140.thread_local
over other ways of defining thread-local data.
-Starting with C++11, variables can be declared with the +
Variables can be declared with the
thread_local
specifier:
thread_local Foo foo = ...;@@ -1022,8 +1027,8 @@ thread, rather than once at program startup. This means that other
thread_local
variables are subject to the same
initialization-order issues as static variables (and more besides).
-thread_local
variable instances are destroyed when their thread
-terminates, so they do not have the destruction-order issues of static
+
thread_local
variable instances are not destroyed before their
+thread terminates, so they do not have the destruction-order issues of static
variables.
thread_local
variable scales with
the number of running threads (in the worst case), which can be quite large
in a program.thread_local
.thread_local
.thread_local
may not be as efficient as certain compiler
intrinsics.The explicit
keyword can be applied to a constructor
-or (since C++11) a conversion operator, to ensure that it can only be
+or 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); ... @@ -1395,13 +1401,10 @@ recommended that you design your class so that the default implementation of those operations is correct. Remember to review the correctness of any defaulted operations as you would any other code. -Due to the risk of slicing, prefer to avoid providing a public assignment -operator or copy/move constructor for a class that's -intended to be derived from (and prefer to avoid deriving from a class -with such members). If your base class needs to be -copyable, provide a public virtual
+Clone()
-method, and a protected copy constructor that derived classes -can use to implement it.To eliminate the risk of slicing, prefer to make base classes abstract, +by making their constructors protected, by declaring their destructors protected, +or by giving them one or more pure virtual member functions. Prefer to avoid +deriving from concrete classes.
@@ -1505,7 +1508,9 @@ which are prone to ambiguity, confusion, and outright bugs.All inheritance should be
+an instance of the base class as a member instead. You may use +public
. If you want to do private inheritance, you should be including -an instance of the base class as a member instead.final
on classes when you don't intend to support using +them as base classes.Do not overuse implementation inheritance. Composition is often more appropriate. Try to restrict use of @@ -1700,10 +1705,11 @@ sections that would be empty.
Within each section, prefer grouping similar kinds of declarations together, and prefer the -following order: types (including
+following order: types and type aliases (typedef
, -using
,enum
, and nested structs and classes), -constants, factory functions, constructors and assignment -operators, destructor, all other methods, data members.typedef
, +using
,enum
, nested structs and classes), +static constants, factory functions, constructors and assignment +operators, destructor, all other member andfriend
functions, +data members.Do not put large method definitions inline in the class definition. Usually, only trivial or @@ -1731,7 +1737,7 @@ Avoid returning a pointer unless it can be null.
function, or both. Non-optional input parameters should usually be values orconst
references, while non-optional output and input/output parameters should usually be references (which cannot be null). -Generally, useabsl::optional
to represent optional by-value +Generally, usestd::optional
to represent optional by-value inputs, and use aconst
pointer when the non-optional form would have used a reference. Use non-const
pointers to represent optional outputs and optional input/output parameters. @@ -1890,7 +1896,7 @@ doubt, use overloads. form, the return type appears before the function name. For example:int foo(int x);-The newer form, introduced in C++11, uses the
auto
+The newer form uses the
@@ -1976,7 +1982,7 @@ can be used to automate ownership bookkeeping, to ensure these responsibilities are met.auto
keyword before the function name and a trailing return type after the argument list. For example, the declaration above could equivalently be written:std::unique_ptr
is a smart pointer type -introduced in C++11, which expresses exclusive ownership +which expresses exclusive ownership of a dynamically allocated object; the object is deleted when thestd::unique_ptr
goes out of scope. It cannot be copied, but can be moved to @@ -2036,7 +2042,7 @@ all copies, and the object is deleted when the last where the resource releases take place.
std::unique_ptr
expresses ownership
- transfer using C++11's move semantics, which are
+ transfer using move semantics, which are
relatively new and may confuse some programmers.This prohibition also applies to the exception handling related
-features added in C++11, such as
-std::exception_ptr
and
+
This prohibition also applies to exception handling related
+features such as std::exception_ptr
and
std::nested_exception
.
There is an exception to @@ -2546,7 +2551,14 @@ casts when explicit type conversion is necessary. will not compile if conversion can result in information loss. The syntax is also concise.
absl::implicit_cast
+ to safely cast up a type hierarchy,
+ e.g., casting a Foo*
to a
+ SuperclassOfFoo*
or casting a
+ Foo*
to a const Foo*
. C++
+ usually does this automatically but some situations
+ need an explicit up-cast, such as use of the
+ ?:
operator.static_cast
as the equivalent of a C-style cast
that does value conversion, when you need to
@@ -2668,10 +2680,8 @@ localization, and security hardening.
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 (see e.g.,
-
-absl/strings
)
-rather than
+Use explicit formatting functions (such as
+absl::StreamFormat()
) rather than
stream manipulators or formatting flags to control formatting
details such as number base, precision, or padding.
constexpr
. Do not use
is
int
. If a program needs a variable of a
different size, use a precise-width integer type from
-<stdint.h>
, such as
+<cstdint>
, such as
int16_t
. If your variable represents a
value that could ever be greater than or equal to 2^31
(2GiB), use a 64-bit type such as int64_t
.
@@ -2858,7 +2868,7 @@ calculations which may require a larger type. When in doubt,
choose a larger type.
-C++ does not specify the sizes of integer types +
C++ does not precisely specify the sizes of integer types
like int
. Typically people assume
that short
is 16 bits,
int
is 32 bits, long
is 32 bits
@@ -3279,13 +3289,13 @@ auto i = y.Find(key);
by eliminating type information that is obvious or irrelevant, so that
the reader can focus on the meaningful parts of the code:
std::unique_ptr<WidgetWithBellsAndWhistles> widget_ptr = - absl::make_unique<WidgetWithBellsAndWhistles>(arg1, arg2); + std::make_unique<WidgetWithBellsAndWhistles>(arg1, arg2); absl::flat_hash_map<std::string, std::unique_ptr<WidgetWithBellsAndWhistles>>::const_iterator it = my_map_.find(key); std::array<int, 6> numbers = {4, 8, 15, 16, 23, 42};-
auto widget_ptr = absl::make_unique<WidgetWithBellsAndWhistles>(arg1, arg2); +auto widget_ptr = std::make_unique<WidgetWithBellsAndWhistles>(arg1, arg2); auto it = my_map_.find(key); std::array numbers = {4, 8, 15, 16, 23, 42};@@ -3450,18 +3460,18 @@ ordering of fields than thePoint
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 draft C++ standard. They are on track for publishing in C++20.
+into the C++ standard, being added as part of C++20. -The rules in the draft C++ standard are stricter than in C and compiler -extensions, requiring that the designated initializers appear in the same order -as the fields appear in the struct definition. So in the example above it is -legal according to draft C++20 to initialize
+x
and then -z
, but noty
and thenx
.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 +fields appear in the struct definition. So in the example above, it is legal +according to C++20 to initialize
x
and thenz
, but not +y
and thenx
.Use designated initializers only in the form that is compatible with the -draft C++20 standard: with initializers in the same order as the corresponding -fields appear in the struct definition.
+C++20 standard: with initializers in the same order as the corresponding fields +appear in the struct definition. @@ -3549,8 +3559,8 @@ std::sort(indices.begin(), indices.end(), [&](int a, int b) {
this
by value,
+ since the use of this
is often implicit.
[&]
) only when
+the lifetime of the lambda is obviously shorter than any potential
captures.
[=]
) only as a means of
+binding a few variables for a short lambda, where the set of captured
+variables is obvious at a glance, and which does not result in
+capturing this
implicitly. (That means that a lambda that
+appears in a non-static class member function and refers to non-static
+class members in its body must capture this
explicitly or
+via [&]
.) Prefer not to write long or complex lambdas
+with default capture by value.
Do not define specializations of std::hash
.
std::hash<T>
is the function object that the
-C++11 hash containers use to hash keys of type T
,
-unless the user explicitly specifies a different hash function. For
-example, std::unordered_map<int, std::string>
is a hash
-map that uses std::hash<int>
to hash its keys,
-whereas std::unordered_map<int, std::string, MyIntHash>
-uses MyIntHash
.
std::hash
is defined for all integral, floating-point,
-pointer, and enum
types, as well as some standard library
-types such as string
and unique_ptr
. Users
-can enable it to work for their own types by defining specializations
-of it for those types.
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
.
All global variables should have a comment describing what they -are, what they are used for, and (if unclear) why it needs to be +are, what they are used for, and (if unclear) why they need to be global. For example:
// The total number of test cases that we run through in this regression test. @@ -4618,33 +4563,7 @@ non-obvious, interesting, or important parts of your code.Explanatory Comments
Tricky or complicated code blocks should have comments -before them. Example:
- -// Divide result by two, taking into account that x -// contains the carry from the add. -for (int i = 0; i < result->size(); ++i) { - x = (x << 8) + (*result)[i]; - (*result)[i] = x >> 1; - x &= 1; -} -- -Line-end Comments
- -Also, lines that are non-obvious should get a comment -at the end of the line. These end-of-line comments should -be separated from the code by 2 spaces. Example:
- -// If we have enough memory, mmap the data portion too. -mmap_budget = max<int64_t>(0, mmap_budget - index_->length()); -if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock)) - return; // Error already logged. -- -Note that there are both comments that describe what -the code is doing, and comments that mention that an -error has already been logged when the function -returns.
+before them.Function Argument Comments
@@ -4662,7 +4581,8 @@ one of the following remedies: values self-describing.
You shouldn't use the C++11 char16_t
and
+
You shouldn't use char16_t
and
char32_t
character types, since they're for
non-UTF-8 text. For similar reasons you also shouldn't
use wchar_t
(unless you're writing code that
@@ -5630,7 +5550,9 @@ trailing whitespace at the end of a line.
void f(bool b) { // Open braces should always have a space before them. +int i = 0; // Two spaces before end-of-line comments. + +void f(bool b) { // Open braces should always have a space before them. ... int i = 0; // Semicolons usually have no space before them. // Spaces inside braces for braced-init-list are optional. If you use them, @@ -5865,32 +5787,6 @@ occasionally need to break on Windows:resource.h
and contain only macros, do not need to conform to these style guidelines.
Use common sense and BE CONSISTENT.
- -If you are editing code, take a few minutes to look at the
-code around you and determine its style. If they use spaces
-around their if
clauses, you should, too. If their
-comments have little boxes of stars around them, make your
-comments have little boxes of stars around them too.
The point of having style guidelines is to have a common -vocabulary of coding so people can concentrate on what you are -saying, rather than on how you are saying it. We present global -style rules here so people know the vocabulary. But local style -is also important. If code you add to a file looks drastically -different from the existing code around it, the discontinuity -throws readers out of their rhythm when they go to read it. Try -to avoid this.
- - - -OK, enough writing about writing code; the code itself is much -more interesting. Have fun!
- -