mirror of
https://github.com/isocpp/CppCoreGuidelines.git
synced 2024-03-22 13:30:58 +08:00
Reorganized the Type safety profile
This commit is contained in:
parent
c721b2c325
commit
9d44e718eb
|
@ -1,6 +1,6 @@
|
|||
# <a name="main"></a>C++ Core Guidelines
|
||||
|
||||
May 21, 2017
|
||||
May 22, 2017
|
||||
|
||||
|
||||
Editors:
|
||||
|
@ -9424,6 +9424,7 @@ Expression rules:
|
|||
* [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.63: Don't slice](#Res-slice)
|
||||
* [ES.64: Use the `T{e}`notation for construction](#Res-construct)
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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 `#include <cstdarg>` and `#include <stdarg.h>`
|
||||
|
||||
|
||||
## ES.stmt: Statements
|
||||
|
||||
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.
|
||||
|
||||
### <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="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 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).
|
||||
* <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).
|
||||
* <a name="Pro-type-cstylecast"></a>Type.4: Don't use C-style `(T)expression` casts:
|
||||
[Prefer static casts](#Res-cast-named).
|
||||
* [Type.4.1: Don't use `T(expression)` cast](#Pro-fct-style-cast):
|
||||
[Prefer named casts](#Res-casts-named).
|
||||
* [Type.5: Don't use a variable before it has been initialized](#Pro-type-init):
|
||||
* <a name="Pro-type-cstylecast"></a>Type.4: Don't use C-style `(T)expression` or functional `T(expression)` casts:
|
||||
Prefer [construction](#Res-construct) or [named casts](#Res-cast-named).
|
||||
* <a name="Pro-type-init"></a>Type.5: Don't use a variable before it has been initialized:
|
||||
[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),
|
||||
possibly using [default constructors](#Rc-default0) or
|
||||
[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).
|
||||
* [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).
|
||||
|
||||
##### 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).
|
||||
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
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user