From 6a3d61fd8dd746739180c3578c1157b94220944a Mon Sep 17 00:00:00 2001 From: Bjarne Stroustrup Date: Sun, 9 Apr 2017 15:15:48 -0400 Subject: [PATCH] a few more fix-ups in ES.* --- CppCoreGuidelines.md | 64 +++++++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index f71c716..68bba56 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1,6 +1,6 @@ # C++ Core Guidelines -April 4, 2017 +April 9, 2017 Editors: @@ -9770,19 +9770,16 @@ For containers, there is a tradition for using `{...}` for a list of elements an ##### Note -Initialization of a variable declared using `auto` with a single value, e.g., `{v}`, had surprising results until recently: +Initialization of a variable declared using `auto` with a single value, e.g., `{v}`, had surprising results until C++17. +The C++17 rules are somewhat less surprising: auto x1 {7}; // x1 is an int with the value 7 - // x2 is an initializer_list with an element 7 - // (this will will change to "element 7" in C++17) - auto x2 = {7}; + auto x2 = {7}; // x2 is an initializer_list with an element 7 auto x11 {7, 8}; // error: two initializers auto x22 = {7, 8}; // x2 is an initializer_list with elements 7 and 8 -##### Exception - -Use `={...}` if you really want an `initializer_list` +So use `={...}` if you really want an `initializer_list` auto fib10 = {0, 1, 2, 3, 5, 8, 13, 21, 34, 55}; // fib10 is a list @@ -9826,12 +9823,16 @@ increases readability, and it has zero or near zero runtime cost. { auto p1 = make_unique(7); // OK int* p2 = new int{7}; // bad: might leak - // ... + // ... no assignment to p2 ... if (leak) return; + // ... no assignment to p2 ... + vector v(7); + v.at(7) = 0; // exception thrown // ... } If `leak == true` the object pointed to by `p2` is leaked and the object pointed to by `p1` is not. +The same is the case when `at()` throws. ##### Enforcement @@ -10373,6 +10374,7 @@ consider `gsl::finally()` as a cleaner and more reliable alternative to `goto ex ##### Reason In a non-trivial loop body, it is easy to overlook a `break` or a `continue`. + A `break` in a loop has a dramatically different meaning than a `break` in a `switch`-statement (and you can have `switch`-statement in a loop and a loop in a `switch`-case). @@ -10861,11 +10863,29 @@ Flag uses of `0` and `NULL` for pointers. The transformation may be helped by si ##### Reason -Casts are a well-known source of errors. Makes some optimizations unreliable. +Casts are a well-known source of errors. Make some optimizations unreliable. -##### Example +##### Example, bad - ??? + double d = 2; + auto p = (long*)&d; + auto q = (long long*)&d; + cout << d << ' ' << *p << ' ' << *q << '\n'; + +What would you think this fragment prints? The result is at best implementation defined. I got + + 2 0 4611686018427387904 + +Adding + + *q = 666; + cout << d << ' ' << *p << ' ' << *q << '\n'; + +I got + + 3.29048e-321 666 666 + +Surprised? I'm just glad I didn't crash the program. ##### Note @@ -10910,15 +10930,29 @@ The named casts are: ##### Example - ??? + class B { /* ... */ }; + class D { /* ... */ }; + + template D* upcast(B* pb) + { + D* pd0 = pb; // error: no implicit conversion from B* to D* + D* pd1 = (D*)pb; // legal, but what is done? + D* pd2 = static_cast(pb); // error: D is not derived from B + D* pd3 = reinterpret_cast(pb); // OK: on your head be it! + D* pd4 = dynamic_cast(pb); // OK: return nullptr + // ... + } + +The example was synthesized from real-world bugs where `D` used to be derived from `B`, but someone refactored the hierarchy. +The C-style cast is dangerous because it can do any kind of conversion, depriving us of any protection from mistakes (now or in the future). ##### Note When converting between types with no information loss (e.g. from `float` to `double` or `int64` from `int32`), brace initialization may be used instead. - double d{some_float}; - int64_t i{some_int32}; + double d {some_float}; + int64_t i {some_int32}; This makes it clear that the type conversion was intended and also prevents conversions between types that might result in loss of precision. (It is a