From 9d44e718eb5905f2b914d089169fcd6d6a7233d0 Mon Sep 17 00:00:00 2001 From: Bjarne Stroustrup Date: Tue, 23 May 2017 14:38:52 -0400 Subject: [PATCH] Reorganized the Type safety profile --- CppCoreGuidelines.md | 143 ++++++++++++++++++++++++++----------------- 1 file changed, 87 insertions(+), 56 deletions(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 989d59a..4abed4f 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1,6 +1,6 @@ # 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 ` and `#include ` + ## 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. +### 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 f(complex); + + 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. + ## Arithmetic ### ES.100: Don't mix signed and unsigned arithmetic @@ -18985,23 +19064,21 @@ Type safety profile summary: * Type.1: Don't use `reinterpret_cast`: A strict version of [Avoid casts](#Res-casts) and [prefer named casts](#Res-casts-named). -* Type.2: Don't use `static_cast` downcasts: +* Type.2: Don't use `static_cast` to downcast: [Use `dynamic_cast` instead](#Rh-dynamic_cast). * Type.3: Don't use `const_cast` to cast away `const` (i.e., at all): [Don't cast away const](#Res-casts-const). -* 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): +* Type.4: Don't use C-style `(T)expression` or functional `T(expression)` casts: +Prefer [construction](#Res-construct) or [named casts](#Res-cast-named). +* 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): +* 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): +* Type.7: Avoid naked union: [Use `variant` instead](#Ru-naked). -* [Type.8: Avoid varargs](#Pro-type-varargs): +* 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. -### 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. - - -### 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 `{}`. - - ## Pro.bounds: Bounds safety profile