First of all: What is a profile according to the C++ Core Guidelines? Here is their definition: “A ‘profile’ is a set of deterministic and portably enforceable subset rules (i.e., restrictions) that are designed to achieve a specific guarantee.”
Two terms in this definition are particularly interesting:
Deterministic: The profiles require only local analysis that can be implemented by a compiler.
Portably enforceable: Different tools on different platforms give you the same answer.
There are two main reasons for the profiles:
You have to deal with legacy code, and you cannot apply all rules of the C++ Core Guidelines in one step. You have to apply the rules step by step and, therefore, use some rules first and some rules later.
Some related rules may be more important to your code base than others. They aim for a specific goal such as the “avoidance of bounds errors” or the “correct usage of types.” These related rules are called profiles.
The C++ Core Guidelines provide profiles for type safety, bounds safety, and lifetime safety, which can be automatically checked. Read more details about automatic checks in Appendix A, Enforcing the C++ Core Guidelines.
The following sections give a concise overview of the three profiles.
Type safety: Use the types correctly, and therefore, avoid unsafe casts and unions.
Type safety consists of eight rules, which are prefixed by type. The rules start with “Don’t,” “Always,” or “Avoid” and refer to existing rules.
Type.1: Avoid casts
Don’t use reinterpret_cast
: ES.48: Avoid casts and ES.49: If you must use a cast, use a named cast
Don’t use static_cast
for arithmetic types: ES.48: Avoid casts and ES.49: If you must use a cast, use a named cast
Don’t cast between pointer types where the source type and the target type are the same: ES.48: Avoid casts
Don’t cast between pointer types when the conversion could be implicit: ES.48: Avoid casts
Type.2: Don’t use static_cast
to downcast: C.146: Use dynamic_cast
where class hierarchy navigation is unavoidable
Type.3: Don’t use const_cast
to cast away const
: ES.50: Don’t cast away const
Type.4: Don’t use C-style (T)expression
or functional T(expression)
casts: ES.34: Prefer the {}-initializer syntax and ES.49: If you must use a cast, use a named cast
Type.5: Don’t use a variable before it has been initialized: ES.20: Always initialize an object
Type.6: Always initialize a member variable: ES.20: Always initialize an object, C.43: Ensure that a copyable (value type) class has a default constructor, and C.45: Don’t define a default constructor that only initializes data members; use member initializers instead
Type.7: Avoid naked union: C.181: Avoid “naked” unions
Type.8: Avoid va_arg
s: F.55: Don’t use va_arg arguments
Bounds safety: Operate inside the bounds of allocated memory.
The two enemies for bounds safety are pointer arithmetic and array indexing. Additionally, when you use a pointer, it should only address a single object but not an array. To make the profile bounds safety complete, you should combine it with the rules to type safety and lifetime safety.
Bounds safety consists of four rules:
Bounds.1: Don’t use pointer arithmetic: I.13: Do not pass an array as a single pointer and ES.42: Keep use of pointers simple and straightforward
Bounds.2: Only index into arrays using constant expressions: I.13: Do not pass an array as a single pointer and ES.42: Keep use of pointers simple and straightforward
Bounds.3: No array-to-pointer decay: I.13: Do not pass an array as a single pointer and ES.42: Keep use of pointers simple and straightforward
Bounds.4: Don’t use standard-library functions and types that are not bounds-checked: SL.con.3: Avoid bounds error
Lifetime safety: Dereference only a valid pointer.
A pointer is invalid if, for example, the pointer is uninitialized, is a std::nullptr
, points outside the range of an array, or points to a deleted object. The profile lifetime safety consists of one rule:
Lifetime.1: Don’t dereference a possibly invalid pointer: ES.65: Don’t dereference an invalid pointer