Skip to content
Snippets Groups Projects
Commit c21db7c0 authored by scott snyder's avatar scott snyder
Browse files

more c++20 updates

parent c99c922f
No related branches found
No related tags found
No related merge requests found
...@@ -155,7 +155,7 @@ package you're working on. But please try to always follow these rules: ...@@ -155,7 +155,7 @@ package you're working on. But please try to always follow these rules:
An exception for this is xAOD data classes, where the member names are An exception for this is xAOD data classes, where the member names are
exposed via ROOT for analysis. exposed via ROOT for analysis.
- *Do not start any other names with* =m_=. [<<m-prefix-reserved>>] - *Do not start any other names with* =m_=. [<<m-prefix-reserved>>]
...@@ -208,14 +208,14 @@ class Track; ...@@ -208,14 +208,14 @@ class Track;
enum State { green, yellow, red }; enum State { green, yellow, red };
#+END_EXAMPLE #+END_EXAMPLE
- *Typedef names should start with an uppercase letter if they are public - *Type alias (typedef) names should start with an uppercase letter if they are public
and treated as classes.* [<<typedef-naming>>] and treated as classes.* [<<typedef-naming>>]
#+BEGIN_EXAMPLE #+BEGIN_EXAMPLE
typedef vector<MCParticleKinematics*> TrackVector; using TrackVector = vector<MCParticleKinematics*>;
#+END_EXAMPLE #+END_EXAMPLE
- *Alternatively, a typedef name may start with a lower-case letter - *Alternatively, a type alias (typedef) name may start with a lower-case letter
and end with* =_t=. [<<typedef-naming-2>>] and end with* =_t=. [<<typedef-naming-2>>]
This form should be reserved for type names which are not treated This form should be reserved for type names which are not treated
...@@ -223,7 +223,7 @@ typedef vector<MCParticleKinematics*> TrackVector; ...@@ -223,7 +223,7 @@ typedef vector<MCParticleKinematics*> TrackVector;
are private to a class. are private to a class.
#+BEGIN_EXAMPLE #+BEGIN_EXAMPLE
typedef unsigned int mycounter_t; using mycounter_t = unsigned int;
#+END_EXAMPLE #+END_EXAMPLE
...@@ -347,7 +347,7 @@ public: ...@@ -347,7 +347,7 @@ public:
details which are inside its header. This saves time in compilation details which are inside its header. This saves time in compilation
and avoids an apparent dependency upon the =Line= header file. and avoids an apparent dependency upon the =Line= header file.
Be careful, however: this does not work if =Line= is actually a =typedef= Be careful, however: this does not work if =Line= is actually an alias
(as is the case, for example, with many of the xAOD classes). (as is the case, for example, with many of the xAOD classes).
- *Each header file must contain the declaration for one class only, except for - *Each header file must contain the declaration for one class only, except for
...@@ -457,8 +457,8 @@ vector<int> x; // Missing std! ...@@ -457,8 +457,8 @@ vector<int> x; // Missing std!
Otherwise, the =using= may serve to hide problems with missing Otherwise, the =using= may serve to hide problems with missing
namespace qualifications in the headers. namespace qualifications in the headers.
This rule does not apply when =using= is used to define a new name This rule does not apply when =using= is used to define a type alias
for a type (similarly to =typedef=). (similarly to =typedef=).
** Control flow ** Control flow
...@@ -497,6 +497,16 @@ for (std::vector<int>::const_iterator it = v.begin(); ...@@ -497,6 +497,16 @@ for (std::vector<int>::const_iterator it = v.begin();
multiple iterators within the loop. But most simple loops over multiple iterators within the loop. But most simple loops over
STL ranges are more simply written with a range-based for. STL ranges are more simply written with a range-based for.
As of C++20, you can initialize additional variables in a
range-based for:
#+BEGIN_EXAMPLE
void foo (const std::vector<float>& v) {
for (int i = 0; float f : v) {
std::cout << i++ << " " << f << "\n";
}
}
#+END_EXAMPLE
- *Switch statements should have a default clause.* [<<switch-default>>] - *Switch statements should have a default clause.* [<<switch-default>>]
...@@ -655,6 +665,20 @@ void A::foo() ...@@ -655,6 +665,20 @@ void A::foo()
defining a symbol for it. defining a symbol for it.
- *Use <numbers> header for mathematical constants.* [<<math-constants>>]
Basic mathematical constants are available in the header
=<numbers>=. Use these in preference to the =M_= constants from
=math.h= or explicit definitions:
#+BEGIN_EXAMPLE
#include <numbers>
#include <cmath>
double f (double x) {
return std::sin (x * std::numbers::pi);
}
#+END_EXAMPLE
- *Declare each type of variable in a separate declaration statement, and - *Declare each type of variable in a separate declaration statement, and
do not declare different types (e.g. int and int pointer) in one declaration statement.* [<<separate-declarations>>] do not declare different types (e.g. int and int pointer) in one declaration statement.* [<<separate-declarations>>]
...@@ -1211,7 +1235,18 @@ int convertAndBuffer (int* buf, float x) ...@@ -1211,7 +1235,18 @@ int convertAndBuffer (int* buf, float x)
#+END_EXAMPLE #+END_EXAMPLE
(As a special case, you can safely convert any pointer type to or from (As a special case, you can safely convert any pointer type to or from
a =char*=.) The proper way to do such a conversion is with a union. a =char*=.) The proper way to do such a conversion is with
a =std::bit_cast=:
#+BEGIN_EXAMPLE
#include <bit>
int convertAndBuffer (int* buf, float x)
{
*buf = std::bit_cast<int> (x);
return *buf;
}
#+END_EXAMPLE
Prior to C++20, the recommended way to do this was with a union,
but that should not be used for new code.
** The class interface ** The class interface
...@@ -1423,6 +1458,38 @@ a = b = c; ...@@ -1423,6 +1458,38 @@ a = b = c;
will assign =c= to =b= and then =b= to =a= as is the case with built-in objects. will assign =c= to =b= and then =b= to =a= as is the case with built-in objects.
- *Use* =std::span= *to represent and pass a bounded region of memory.* [<<span>>]
In particular, use =std::span= instead of passing a pointer with
a separate element count (or even worse, a pointer to an array with
no bounds information).
So you can use this:
#+BEGIN_EXAMPLE
#include <span>
int sum (const std::span<int>& s)
{
int ret = 0;
for (int i : s) ret += i;
return ret;
}
#+END_EXAMPLE
instead of
#+BEGIN_EXAMPLE
int sum (const int* p, size_t n)
{
int ret = 0;
for (size_t i = 0; i < n; i++) ret += p[i];
return ret;
}
#+END_EXAMPLE
One might expect that =std::span= would include an =at()= method,
to allow indexing with bounds checking, but that is only available
in C++23. In the meantime, =CxxUtils::span= is very similar to
=std::span= but does implement =at()=.
*** =const= correctness *** =const= correctness
...@@ -1493,6 +1560,56 @@ ostream& operator<<(ostream& out, const String& s); ...@@ -1493,6 +1560,56 @@ ostream& operator<<(ostream& out, const String& s);
closely related member functions is very confusing and is not closely related member functions is very confusing and is not
recommended. recommended.
*** Comparisons
- *Define comparions for custom types using* =operator=== *and* =operator<=>=. [<<comparisons>>]
Comparions of for a custom class should be written using
=operator=== (for equality/inequality) and =operator<=>= (for ordering).
The compiler will supply the other comparison operators
(=operator!==, =operator<=, etc.) automatically. Where possible,
=operator<=>= is best defined using the same operator on the
members involved. Examples:
#+BEGIN_EXAMPLE
#include <compare>
#include <tuple>
class S
{
public:
bool operator== (const S& other)
{
return m_key == other.m_key;
}
std::strong_ordering operator<=> (const S& other)
{
return m_key <=> other.m_key;
}
private:
int m_key;
};
class Version
{
public:
bool operator== (const Version& other)
{
return m_major == other.m_major && m_minor == other.m_minor;
}
std::strong_ordering operator<=> (const Version& other)
{
return std::make_tuple (m_major, m_minor) <=>
std::make_tuple (other.m_major, other.m_minor);
}
private:
int m_major;
int m_minor;
};
#+END_EXAMPLE
** =new= and =delete= ** =new= and =delete=
...@@ -2340,7 +2457,7 @@ safer possibilities. ...@@ -2340,7 +2457,7 @@ safer possibilities.
=scanf= and =printf= are not type-safe and they are not =scanf= and =printf= are not type-safe and they are not
extensible. Use =operator>>= and =operator<<= associated with C++ streams extensible. Use =operator>>= and =operator<<= associated with C++ streams
instead, along with =std::format= to handle formatting (see [[use-format]]). instead, along with =std::format= to handle formatting (see use-format [[use-format]]).
iostream and stdio functions should never be mixed. iostream and stdio functions should never be mixed.
Example: Example:
...@@ -2548,7 +2665,7 @@ some machines, left-to-right on others. -- end note ] ...@@ -2548,7 +2665,7 @@ some machines, left-to-right on others. -- end note ]
That way, they won't clutter up the header file. That way, they won't clutter up the header file.
- *Do not declare your own typedef for booleans. Use the bool type of C++ for booleans.* [<<use-bool>>] - *Do not declare your own alias for booleans. Use the bool type of C++ for booleans.* [<<use-bool>>]
The =bool= type was not implemented in C. Programmers usually got around The =bool= type was not implemented in C. Programmers usually got around
the problem by typedefs and/or const declarations. This is no longer the problem by typedefs and/or const declarations. This is no longer
...@@ -2638,37 +2755,37 @@ some machines, left-to-right on others. -- end note ] ...@@ -2638,37 +2755,37 @@ some machines, left-to-right on others. -- end note ]
level. level.
- *Avoid using typedef to create aliases for classes.* [<<avoid-typedef>>] - *Avoid creating type aliases for classes.* [<<avoid-typedef>>]
Typedefs are a serious impediment in large systems. While they Type aliases (typedefs) are a serious impediment in large systems. While they
simplify code for the original author, a system filled with typedefs simplify code for the original author, a system filled with aliases
can be difficult to understand. If the reader encounters a class =A=, he can be difficult to understand. If the reader encounters a class =A=, he
or she can find an =#include= with ``A.h'' in it to locate a description of =A=; or she can find an =#include= with ``A.h'' in it to locate a description of =A=;
but typedefs carry no context that tell a reader where to find a but aliases carry no context that tell a reader where to find a
definition. Moreover, most of the generic characteristics obtained definition. Moreover, most of the generic characteristics obtained
with typedefs are better handled by object oriented techniques, like with aliases are better handled by object oriented techniques, like
polymorphism. polymorphism.
Typedefs are acceptible where they provide part of the expected Aliases are acceptible where they provide part of the expected
interface for a class, for example =value_type=, etc. interface for a class, for example =value_type=, etc.
in classes used with STL algorithms. They are often indispensible in classes used with STL algorithms. They are often indispensible
in template programming and metaprogramming, and are also part in template programming and metaprogramming, and are also part
of how xAOD classes and POOL converters are typically defined. of how xAOD classes and POOL converters are typically defined.
In other contexts, they should be used with care, and should generally In other contexts, they should be used with care, and should generally
be accompanied with a comment giving the rationale for the typedef. be accompanied with a comment giving the rationale for the alias.
Typedefs may be used as a ``customization point;'' that is, to allow Aliases may be used as a ``customization point;'' that is, to allow
the possibility of changing a type in the future. the possibility of changing a type in the future.
For example, the auxiliary store code uses integers to identify particular For example, the auxiliary store code uses integers to identify particular
auxiliary data items. But rather than declaring these as an integer type auxiliary data items. But rather than declaring these as an integer type
directly, a typedef =auxid_t= is used. This allows for the possibility directly, an alias =auxid_t= is used. This allows for the possibility
of changing the type in the future without having to make changes of changing the type in the future without having to make changes
throughout the code base. It also makes explicit that variables throughout the code base. It also makes explicit that variables
of that type are meant to identify auxiliary data items, of that type are meant to identify auxiliary data items,
rather than being random integers. rather than being random integers.
A typedef may also be used inside a function body to shorten An alias may also be used inside a function body to shorten
a cumbersome type name; however, this should be used sparingly. a cumbersome type name; however, this should be used sparingly.
...@@ -3008,6 +3125,20 @@ foo( 1, 2, 3 ) ...@@ -3008,6 +3125,20 @@ foo( 1, 2, 3 )
fix the whole thing, or code to match. fix the whole thing, or code to match.
- *Prefer* =using= *to* =typedef=*.* [<<prefer-using>>]
To declare a type alias, prefer the newer =using= syntax:
#+BEGIN_EXAMPLE
using Int_t = int;
#+END_EXAMPLE
to the =typedef= syntax:
#+BEGIN_EXAMPLE
typedef int Int_t;
#+END_EXAMPLE
The =using= syntax makes it clearer what is being defined; it can also
be used to declare templated aliases.
** Comments ** Comments
- *Use Doxygen style comments before class/method/data member declarations. - *Use Doxygen style comments before class/method/data member declarations.
...@@ -3130,6 +3261,18 @@ The comment includes the fact that it is the perpendicular distance. ...@@ -3130,6 +3261,18 @@ The comment includes the fact that it is the perpendicular distance.
** Version 0.8 ** Version 0.8
- Updated for C++20. - Updated for C++20.
- Don't use modules or coroutines.
- Add recommendation to use =<numbers>=.
- Suggest using =auto= to move the return type to the end of a method
signature when returning types defined within the class.
- Suggest not defining template functions without the =template= keyword.
- Recommend =std::format= for formatted output.
- Note that range-for can have init-statements.
- Mention =std::bit_cast=.
- Recommend =using= instead of =typedef=. Rephrase previous references
to =typedef=.
- Comparisons should be defined in terms of =operator=== and =operator<=>=.
- Mention =std::span=.
- Some additional references. - Some additional references.
- Clarify that non-ascii characters should not be used in identifier names. - Clarify that non-ascii characters should not be used in identifier names.
- Clarify that variable-length argument lists of varadic template functions - Clarify that variable-length argument lists of varadic template functions
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment