From 901474aa08da04b31510a6ba9bdef15c2f99a28e Mon Sep 17 00:00:00 2001 From: Daniel Cheng Date: Thu, 12 Oct 2023 09:48:56 -0700 Subject: [PATCH] Update C++ style guide for C++20. - Encourage single line nested namespace declarations. - Reference and allow `constinit` in the relevant sections - Update operator overloading guidance for comparison operators: prefer only to overload `==` and optionally `<=>` when there is an obvious ordering, and allow the compiler to derive the other comparison operators. - Discourage prefixing `uint32_t`, et cetera with `std::`. - Document when and how to use concepts: - Use `requires` expressions rather than the alternatives, e.g. a template parameter. - Do not reimplement existing concepts/traits. - Do not expose concepts across API boundaries. - Do not use concepts unnecessarily. - Do not implement concepts that are not compile-time checkable. --- cppguide.html | 203 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 164 insertions(+), 39 deletions(-) diff --git a/cppguide.html b/cppguide.html index ffb061d..487529e 100644 --- a/cppguide.html +++ b/cppguide.html @@ -21,8 +21,8 @@ this power brings with it complexity, which in turn can make code more bug-prone and harder to read and maintain.

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.

C++ Version

-

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.

Header Files

@@ -686,6 +685,11 @@ inline void my_inline_function() { using ::absl::container_internal::ImplementationDetail; + +
  • Single-line nested namespace declarations + + are preferred in new code, but are not required.

    +
  • @@ -940,14 +944,10 @@ Foo a[] = { Foo(1), Foo(2), Foo(3) }; // Fine

    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

    . +Any non-local static storage duration variable that is not so marked should be presumed to have -dynamic initialization, and reviewed very carefully.

    +dynamic initialization, and reviewed very carefully.

    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.

    +are defined consistently.

    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.

    +confuse your users if 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 of constexpr

    +

    Use of constexpr, constinit, and consteval

    Use constexpr to define true -constants or to ensure constant initialization.

    +constants or to ensure constant initialization. +Use 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.

    +variable. Functions can be declared consteval +to restrict their use to compile time.

    Use of constexpr enables definition of @@ -2906,9 +2916,11 @@ in these definitions.

    robust specification of the constant parts of an interface. Use 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.

    Integer Types

    @@ -2948,7 +2960,9 @@ like 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:

  • Prefer not using ## 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.

    +

    Concepts and Constraints

    + +

    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.

    +

    Boost

    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 to typedef, @@ -4308,9 +4429,10 @@ const int kAndroid8_0_0 = 24; // Android 8.0.0

    All 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 of char8_t -rather than char. +rather than char, and will change again in C++23. +

    You shouldn't use char16_t and char32_t character types, since they're for