a few more fix-ups in ES.*

This commit is contained in:
Bjarne Stroustrup
2017-04-09 15:15:48 -04:00
parent 26d56a2f54
commit 6a3d61fd8d

View File

@@ -1,6 +1,6 @@
# <a name="main"></a>C++ Core Guidelines # <a name="main"></a>C++ Core Guidelines
April 4, 2017 April 9, 2017
Editors: Editors:
@@ -9770,19 +9770,16 @@ For containers, there is a tradition for using `{...}` for a list of elements an
##### Note ##### 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 auto x1 {7}; // x1 is an int with the value 7
// x2 is an initializer_list<int> with an element 7 auto x2 = {7}; // x2 is an initializer_list<int> with an element 7
// (this will will change to "element 7" in C++17)
auto x2 = {7};
auto x11 {7, 8}; // error: two initializers auto x11 {7, 8}; // error: two initializers
auto x22 = {7, 8}; // x2 is an initializer_list<int> with elements 7 and 8 auto x22 = {7, 8}; // x2 is an initializer_list<int> with elements 7 and 8
##### Exception So use `={...}` if you really want an `initializer_list<T>`
Use `={...}` if you really want an `initializer_list<T>`
auto fib10 = {0, 1, 2, 3, 5, 8, 13, 21, 34, 55}; // fib10 is a 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<int>(7); // OK auto p1 = make_unique<int>(7); // OK
int* p2 = new int{7}; // bad: might leak int* p2 = new int{7}; // bad: might leak
// ... // ... no assignment to p2 ...
if (leak) return; if (leak) return;
// ... no assignment to p2 ...
vector<int> 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. 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 ##### Enforcement
@@ -10373,6 +10374,7 @@ consider `gsl::finally()` as a cleaner and more reliable alternative to `goto ex
##### Reason ##### Reason
In a non-trivial loop body, it is easy to overlook a `break` or a `continue`. 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 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). (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 ##### 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 ##### Note
@@ -10910,15 +10930,29 @@ The named casts are:
##### Example ##### Example
??? class B { /* ... */ };
class D { /* ... */ };
template<typename D> 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<D*>(pb); // error: D is not derived from B
D* pd3 = reinterpret_cast<D*>(pb); // OK: on your head be it!
D* pd4 = dynamic_cast<D*>(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 ##### Note
When converting between types with no information loss (e.g. from `float` to 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` or `int64` from `int32`), brace initialization may be used instead.
double d{some_float}; double d {some_float};
int64_t i{some_int32}; int64_t i {some_int32};
This makes it clear that the type conversion was intended and also prevents 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 conversions between types that might result in loss of precision. (It is a