diff --git a/cppguide.html b/cppguide.html index e8e308c..59d3682 100644 --- a/cppguide.html +++ b/cppguide.html @@ -165,7 +165,8 @@ input.

C++ Version

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:

  • Other libraries' .h files.
  • + +
  • A blank line
  • @@ -957,10 +960,12 @@ does not make an observable difference. For example:

    Common patterns

    @@ -1164,10 +1170,10 @@ type is defined by a constructor that can take the source type as its only argument (or only argument with no default value).

    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:

    +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 public. If you want to do private inheritance, you should be including -an instance of the base class as a member instead.

    +an instance of the base class as a member instead. You may use +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 typedef, -using, enum, and nested structs and classes), -constants, factory functions, constructors and assignment -operators, destructor, all other methods, data members.

    +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.

    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 or const references, while non-optional output and input/output parameters should usually be references (which cannot be null). -Generally, use absl::optional to represent optional by-value +Generally, use std::optional to represent optional by-value inputs, and use a const 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 auto keyword before the function name and a trailing return type after the argument list. For example, the declaration above could equivalently be written:

    @@ -1976,7 +1982,7 @@ can be used to automate ownership bookkeeping, to ensure these responsibilities are met. 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 the std::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.
  • Shared ownership can be a tempting alternative to @@ -2320,9 +2326,8 @@ exceptions in Google open-source projects as well. Things would probably be different if we had to do it all over again from scratch.

    -

    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.

  • - +
  • Use 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.
  • Use 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.

    @@ -2848,7 +2858,7 @@ enable their use with 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 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 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 not y and then x.

    +

    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 then z, but not +y and then x.

    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) {
  • Default captures by value can be misleading because they do not prevent dangling-pointer bugs. Capturing a pointer by value doesn't cause a deep copy, so it often has the same lifetime issues as capture by reference. - This is especially confusing when capturing 'this' by value, since the use - of 'this' is often implicit.
  • + This is especially confusing when capturing this by value, + since the use of this is often implicit.
  • Captures actually declare new variables (whether or not the captures have initializers), but they look nothing like any other variable declaration @@ -3600,14 +3610,18 @@ prefer to write: // reference.
  • -
  • Use default capture by reference ([&]) only when the -lifetime of the lambda is obviously shorter than any potential +
  • Use default capture by reference ([&]) only when +the lifetime of the lambda is obviously shorter than any potential captures.
  • -
  • Use default capture by value ([=]) only as a means of binding a -few variables for a short lambda, where the set of captured -variables is obvious at a glance. Prefer not to write long or -complex lambdas with default capture by value. +
  • Use default capture by value ([=]) 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.
  • Use captures only to actually capture variables from the enclosing scope. Do not use captures with initializers to introduce new names, or @@ -3789,76 +3803,6 @@ features to the list, so this list may be expanded in the future.

    -

    std::hash

    - -

    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.

    @@ -4491,6 +4435,7 @@ operation.

    preceding it that describe what the function does and how to use it. These comments may be omitted only if the function is simple and obvious (e.g., simple accessors for obvious properties of the class). +Private methods and functions declared in `.cc` files are not exempt. Function comments should be written with an implied subject of This function and should start with the verb phrase; for example, "Opens the file", rather than "Open the file". In general, these comments do not @@ -4603,7 +4548,7 @@ obvious. For example:

    Global Variables

    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.
  • For functions that have several configuration options, consider - defining a single class or struct to hold all the options, + defining a single class or struct to hold all the options + , and pass an instance of that. This approach has several advantages. Options are referenced by name at the call site, which clarifies their meaning. It also reduces @@ -4882,7 +4802,7 @@ encoded as UTF-8, because that will produce incorrect output if the compiler does not interpret the source file as UTF-8.

    -

    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.

    General

    -
    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.
  • - -

    Parting Words

    - -

    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!

    - -
    - + \ No newline at end of file