Reorganized the Type safety profile

This commit is contained in:
Bjarne Stroustrup
2017-05-23 14:38:52 -04:00
parent c721b2c325
commit 9d44e718eb

View File

@@ -1,6 +1,6 @@
# <a name="main"></a>C++ Core Guidelines # <a name="main"></a>C++ Core Guidelines
May 21, 2017 May 22, 2017
Editors: Editors:
@@ -9424,6 +9424,7 @@ Expression rules:
* [ES.61: Delete arrays using `delete[]` and non-arrays using `delete`](#Res-del) * [ES.61: Delete arrays using `delete[]` and non-arrays using `delete`](#Res-del)
* [ES.62: Don't compare pointers into different arrays](#Res-arr2) * [ES.62: Don't compare pointers into different arrays](#Res-arr2)
* [ES.63: Don't slice](#Res-slice) * [ES.63: Don't slice](#Res-slice)
* [ES.64: Use the `T{e}`notation for construction](#Res-construct)
Statement rules: Statement rules:
@@ -10052,6 +10053,29 @@ Creating optimal and equivalent code from all of these examples should be well w
(but don't make performance claims without measuring; a compiler may very well not generate optimal code for every example and (but don't make performance claims without measuring; a compiler may very well not generate optimal code for every example and
there may be language rules preventing some optimization that you would have liked in a particular case). there may be language rules preventing some optimization that you would have liked in a particular case).
##### Example
This rule covers member variables.
class X {
public:
X(int i, int ci) : m2{i}, cm2{ci} {}
// ...
private:
int m1 = 7;
int m2;
int m3;
const int cm1 = 7;
const int cm2;
const int cm3;
};
The compiler will flag the uninitialized `cm3` because it is a `const`, but it will not catch the lack of initialization of `m3`.
Usially, a rare spurious member initialization is worth the absence of errors from lack of initialization and often an optimizer
can elimitate a redundant initialization (e.g., an initialization that occurs immediately before a assignment).
##### Note ##### Note
Complex initialization has been popular with clever programmers for decades. Complex initialization has been popular with clever programmers for decades.
@@ -10591,6 +10615,7 @@ This is basically the way `printf` is implemented.
* Flag definitions of C-style variadic functions. * Flag definitions of C-style variadic functions.
* Flag `#include <cstdarg>` and `#include <stdarg.h>` * Flag `#include <cstdarg>` and `#include <stdarg.h>`
## ES.stmt: Statements ## ES.stmt: Statements
Statements control the flow of control (except for function calls and exception throws, which are expressions). Statements control the flow of control (except for function calls and exception throws, which are expressions).
@@ -11893,6 +11918,60 @@ For example:
Warn against slicing. Warn against slicing.
### <a name="Res-construct"></a>ES.64: Use the `T{e}`notation for construction
##### Reason
The `T{e}` construction syntax makes it explict that construction is desired.
The `T{e}` construction syntax makes doesn't allow narrowing.
`T{e}` is the only safe and general expression for constructing a value of type `T` from an expression `e`.
The casts notations `T(e)` and `(T)e` are neither safe nor general.
##### Example
For built-in types, the construction notation protects against narrowing and reinterpretation
void use(char ch, int i, double d, char* p, long long lng)
{
int x1 = int{ch}; // OK, but redundant
int x2 = int{d}; // error: double->int narrowing; use a cast if you need to
int x3 = int{p}; // error: pointer to->int; use a reinterpret_cast if you really need to
int x4 = int{lng}; // error: long long->int narrowing; use a cast if you need to
int y1 = int(ch); // OK, but redundant
int y2 = int(d); // bad: double->int narrowing; use a cast if you need to
int y3 = int(p); // bad: pointer to->int; use a reinterpret_cast if you really need to
int y4 = int(lng); // bad: long->int narrowing; use a cast if you need to
int z1 = (int)ch; // OK, but redundant
int z2 = (int)d; // bad: double->int narrowing; use a cast if you need to
int z3 = (int)p; // bad: pointer to->int; use a reinterpret_cast if you really need to
int z4 = (int)lng; // bad: long long->int narrowing; use a cast if you need to
}
The integer to/from pointer conversions are implementation defined when usint the `T(e)` or `(T)e` notations, and non-portable
between platforms with different integer and pointer sizes.
##### Note
[Avoid casts](#Res-casts) (explicit type conversion) and if you must [prefer named casts](#Res-casts-named).
##### Note
Whe unambiguous, the `T` can be left out of `T{e}`.
complex<double> f(complex<double>);
auto z = f({2*pi,1});
##### Note
The constructuction notation is the most general [initializer notation](#Res-list).
##### Enforcement
Flag the C-style `(T)e` and functional-style `T(e)` casts.
## <a name="SS-numbers"></a>Arithmetic ## <a name="SS-numbers"></a>Arithmetic
### <a name="Res-mix"></a>ES.100: Don't mix signed and unsigned arithmetic ### <a name="Res-mix"></a>ES.100: Don't mix signed and unsigned arithmetic
@@ -18985,23 +19064,21 @@ Type safety profile summary:
* <a name="Pro-type-reinterpretcast"></a>Type.1: Don't use `reinterpret_cast`: * <a name="Pro-type-reinterpretcast"></a>Type.1: Don't use `reinterpret_cast`:
A strict version of [Avoid casts](#Res-casts) and [prefer named casts](#Res-casts-named). A strict version of [Avoid casts](#Res-casts) and [prefer named casts](#Res-casts-named).
* <a name="Pro-type-downcast"></a>Type.2: Don't use `static_cast` downcasts: * <a name="Pro-type-downcast"></a>Type.2: Don't use `static_cast` to downcast:
[Use `dynamic_cast` instead](#Rh-dynamic_cast). [Use `dynamic_cast` instead](#Rh-dynamic_cast).
* <a name="Pro-type-constcast"></a>Type.3: Don't use `const_cast` to cast away `const` (i.e., at all): * <a name="Pro-type-constcast"></a>Type.3: Don't use `const_cast` to cast away `const` (i.e., at all):
[Don't cast away const](#Res-casts-const). [Don't cast away const](#Res-casts-const).
* <a name="Pro-type-cstylecast"></a>Type.4: Don't use C-style `(T)expression` casts: * <a name="Pro-type-cstylecast"></a>Type.4: Don't use C-style `(T)expression` or functional `T(expression)` casts:
[Prefer static casts](#Res-cast-named). Prefer [construction](#Res-construct) or [named casts](#Res-cast-named).
* [Type.4.1: Don't use `T(expression)` cast](#Pro-fct-style-cast): * <a name="Pro-type-init"></a>Type.5: Don't use a variable before it has been initialized:
[Prefer named casts](#Res-casts-named).
* [Type.5: Don't use a variable before it has been initialized](#Pro-type-init):
[always initialize](#Res-always). [always initialize](#Res-always).
* [Type.6: Always initialize a member variable](#Pro-type-memberinit): * <a name="Pro-type-memberinit"></a>Type.6: Always initialize a member variable:
[always initialize](#Res-always), [always initialize](#Res-always),
possibly using [default constructors](#Rc-default0) or possibly using [default constructors](#Rc-default0) or
[default member initializers](#Rc-in-class-initializers). [default member initializers](#Rc-in-class-initializers).
* [Type.7: Avoid naked union](#Pro-fct-style-cast): * <a name="Pro-type-unon"></a>Type.7: Avoid naked union:
[Use `variant` instead](#Ru-naked). [Use `variant` instead](#Ru-naked).
* [Type.8: Avoid varargs](#Pro-type-varargs): * <a name="Pro-type-varargs"></a>Type.8: Avoid varargs:
[Don't use `va_arg` arguments](#F-varargs). [Don't use `va_arg` arguments](#F-varargs).
##### Impact ##### Impact
@@ -19011,52 +19088,6 @@ Exception may be thrown to indicate errors that cannot be detected statically (a
Note that this type-safety can be complete only if we also have [Bounds safety](#SS-bounds) and [Lifetime safety](#SS-lifetime). Note that this type-safety can be complete only if we also have [Bounds safety](#SS-bounds) and [Lifetime safety](#SS-lifetime).
Without those guarantees, a region of memory could be accessed independent of which object, objects, or parts of objects are stored in it. Without those guarantees, a region of memory could be accessed independent of which object, objects, or parts of objects are stored in it.
### <a name="Pro-fct-style-cast"></a>Type.4.1: Don't use `T(expression)` for casting.
##### Reason
If `e` is of a built-in type, `T(e)` is equivalent to the error-prone `(T)e`.
##### Example, bad
int* p = f(x);
auto i = int(p); // Potential damaging cast; don't or use `reinterpret_cast`
short s = short(i); // potentially narrowing; don't or use `narrow` or `narrow_cast`
##### Note
The {}-syntax makes the desire for construction explicit and doesn't allow narrowing
f(Foo{bar});
##### Enforcement
Flag `T(e)` if used for `e` of a built-in type.
### <a name="Pro-type-memberinit"></a>Type.6: Always initialize a member variable.
##### Reason
Before a variable has been initialized, it does not contain a deterministic valid value of its type. It could contain any arbitrary bit pattern, which could be different on each call.
##### Example
struct X { int i; };
X x;
use(x); // BAD, x has not been initialized
X x2{}; // GOOD
use(x2);
##### Enforcement
* Issue a diagnostic for any constructor of a non-trivially-constructible type that does not initialize all member variables. To fix: Write a data member initializer, or mention it in the member initializer list.
* Issue a diagnostic when constructing an object of a trivially constructible type without `()` or `{}` to initialize its members. To fix: Add `()` or `{}`.
## <a name="SS-bounds"></a>Pro.bounds: Bounds safety profile ## <a name="SS-bounds"></a>Pro.bounds: Bounds safety profile