Merge pull request #696 from tkruse/style-fixes

Style fixes
This commit is contained in:
Gabriel Dos Reis 2016-09-06 01:14:20 -07:00 committed by GitHub
commit db029855dd

View File

@ -3142,10 +3142,10 @@ Here on one popular implementation I got the output:
I expected that because the call of `g()` reuses the stack space abandoned by the call of `f()` so `*p` refers to the space now occupied by `gx`. I expected that because the call of `g()` reuses the stack space abandoned by the call of `f()` so `*p` refers to the space now occupied by `gx`.
Imagine what would happen if `fx` and `gx` were of different types. * Imagine what would happen if `fx` and `gx` were of different types.
Imagine what would happen if `fx` or `gx` was a type with an invariant. * Imagine what would happen if `fx` or `gx` was a type with an invariant.
Imagine what would happen if more that dangling pointer was passed around among a larger set of functions. * Imagine what would happen if more that dangling pointer was passed around among a larger set of functions.
Imagine what a cracker could do with that dangling pointer. * Imagine what a cracker could do with that dangling pointer.
Fortunately, most (all?) modern compilers catch and warn against this simple case. Fortunately, most (all?) modern compilers catch and warn against this simple case.
@ -3400,7 +3400,7 @@ There is not a choice when a set of functions are used to do a semantically equi
##### See also ##### See also
[Default arguments for virtual functions](#Rh-virtual-default-arg} [Default arguments for virtual functions](#Rh-virtual-default-arg)
##### Enforcement ##### Enforcement
@ -7360,7 +7360,7 @@ What we have here is an "invisible" type error that happens to give a result tha
And, talking about "invisible", this code produced no output: And, talking about "invisible", this code produced no output:
v.x = 123; v.x = 123;
cout << v.d << '\n'; // BAD: undefined behavior cout << v.d << '\n'; // BAD: undefined behavior
###### Alternative ###### Alternative
@ -7368,7 +7368,7 @@ Wrap a `union` in a class together with a type field.
The soon-to-be-standard `variant` type (to be found in `<variant>`) does that for you: The soon-to-be-standard `variant` type (to be found in `<variant>`) does that for you:
variant<int,double> v; variant<int, double> v;
v = 123; // v holds an int v = 123; // v holds an int
int x = get<int>(v); int x = get<int>(v);
v = 123.456; // v holds a double v = 123.456; // v holds a double
@ -7394,85 +7394,85 @@ The code is somewhat elaborate.
Handling a type with user-defined assignment and destructor is tricky. Handling a type with user-defined assignment and destructor is tricky.
Saving programmers from having to write such code is one reason for including `variant` in the standard. Saving programmers from having to write such code is one reason for including `variant` in the standard.
class Value { // two alternative representations represented as a union class Value { // two alternative representations represented as a union
private: private:
enum class Tag { number, text }; enum class Tag { number, text };
Tag type; // discriminant Tag type; // discriminant
union { // representation (note: anonymous union) union { // representation (note: anonymous union)
int i; int i;
string s; // string has default constructor, copy operations, and destructor string s; // string has default constructor, copy operations, and destructor
}; };
public: public:
struct Bad_entry { }; // used for exceptions struct Bad_entry { }; // used for exceptions
~Value(); ~Value();
Value& operator=(const Value&); // necessary because of the string variant Value& operator=(const Value&); // necessary because of the string variant
Value(const Value&); Value(const Value&);
// ... // ...
int number() const; int number() const;
string text() const; string text() const;
void set_number(int n); void set_number(int n);
void set_text(const string&); void set_text(const string&);
// ... // ...
}; };
int Value::number() const int Value::number() const
{ {
if (type!=Tag::number) throw Bad_entry{}; if (type != Tag::number) throw Bad_entry{};
return i; return i;
} }
string Value::text() const string Value::text() const
{ {
if (type!=Tag::text) throw Bad_entry{}; if (type != Tag::text) throw Bad_entry{};
return s; return s;
} }
void Value::set_number(int n) void Value::set_number(int n)
{ {
if (type==Tag::text) { if (type == Tag::text) {
s.~string(); // explicitly destroy string s.~string(); // explicitly destroy string
type = Tag::number; type = Tag::number;
} }
i = n; i = n;
} }
void Value::set_text(const string& ss) void Value::set_text(const string& ss)
{ {
if (type==Tag::text) if (type == Tag::text)
s = ss; s = ss;
else { else {
new(&s) string{ss}; // placement new: explicitly construct string new(&s) string{ss}; // placement new: explicitly construct string
type = Tag::text; type = Tag::text;
} }
} }
Value& Value::operator=(const Value& e) // necessary because of the string variant Value& Value::operator=(const Value& e) // necessary because of the string variant
{ {
if (type==Tag::text && e.type==Tag::text) { if (type == Tag::text && e.type == Tag::text) {
s = e.s; // usual string assignment s = e.s; // usual string assignment
return *this; return *this;
} }
if (type==Tag::text) s.~string(); // explicit destroy if (type == Tag::text) s.~string(); // explicit destroy
switch (e.type) { switch (e.type) {
case Tag::number: case Tag::number:
i = e.i; i = e.i;
break; break;
case Tag::text: case Tag::text:
new(&s)(e.s); // placement new: explicit construct new(&s)(e.s); // placement new: explicit construct
type = e.type; type = e.type;
} }
return *this; return *this;
} }
Value::~Value() Value::~Value()
{ {
if (type==Tag::text) s.~string(); // explicit destroy if (type == Tag::text) s.~string(); // explicit destroy
} }
##### Enforcement ##### Enforcement
@ -7504,12 +7504,12 @@ The idea of `Pun` is to be able to look at the character representation of an `i
If you wanted to see the bytes of an `int`, use a (named) cast: If you wanted to see the bytes of an `int`, use a (named) cast:
void if_you_must_pun(int& x) void if_you_must_pun(int& x)
{ {
auto p = reinterpret_cast<unsigned char*>(&x); auto p = reinterpret_cast<unsigned char*>(&x);
cout << p[0] << '\n'; // undefined behavior cout << p[0] << '\n'; // undefined behavior
// ... // ...
} }
Accessing the result of an `reinterpret_cast` to a different type from the objects declared type is still undefined behavior, Accessing the result of an `reinterpret_cast` to a different type from the objects declared type is still undefined behavior,
but at least we can see that something tricky is going on. but at least we can see that something tricky is going on.
@ -7731,9 +7731,9 @@ The default is the easiest to read and write.
enum class Direction : char { n, s, e, w, enum class Direction : char { n, s, e, w,
ne, nw, se, sw }; // underlying type saves space ne, nw, se, sw }; // underlying type saves space
enum class Web_color : int { red = 0xFF0000, enum class Web_color : int { red = 0xFF0000,
green = 0x00FF00, green = 0x00FF00,
blue = 0x0000FF }; // underlying type is redundant blue = 0x0000FF }; // underlying type is redundant
##### Note ##### Note
@ -8460,14 +8460,14 @@ Any type (including primary template or specialization) that overloads unary `*`
##### Example ##### Example
// use Boost's intrusive_ptr // use Boost's intrusive_ptr
#include <boost/intrusive_ptr.hpp> #include<boost/intrusive_ptr.hpp>
void f(boost::intrusive_ptr<widget> p) // error under rule 'sharedptrparam' void f(boost::intrusive_ptr<widget> p) // error under rule 'sharedptrparam'
{ {
p->foo(); p->foo();
} }
// use Microsoft's CComPtr // use Microsoft's CComPtr
#include <atlbase.h> #include<atlbase.h>
void f(CComPtr<widget> p) // error under rule 'sharedptrparam' void f(CComPtr<widget> p) // error under rule 'sharedptrparam'
{ {
p->foo(); p->foo();
@ -9168,7 +9168,7 @@ Reuse of a member name as a local variable can also be a problem:
void S::f(int x) void S::f(int x)
{ {
m=7; // assign to member m = 7; // assign to member
if (x) { if (x) {
int m = 9; int m = 9;
// ... // ...
@ -9487,7 +9487,9 @@ For containers, there is a tradition for using `{...}` for a list of elements an
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 recently:
auto x1 {7}; // x1 is an int with the value 7 auto x1 {7}; // x1 is an int with the value 7
auto x2 = {7}; // x2 is an initializer_list<int> with an element 7 (this will will change to "element 7" in C++17) // 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
@ -9776,7 +9778,7 @@ Requires messy cast-and-macro-laden code to get working right.
##### Example ##### Example
#include <cstdarg> #include<cstdarg>
// "severity" followed by a zero-terminated list of char*s; write the C-style strings to cerr // "severity" followed by a zero-terminated list of char*s; write the C-style strings to cerr
void error(int severity ...) void error(int severity ...)
@ -10364,7 +10366,7 @@ A key example is basic narrowing:
double d = 7.9; double d = 7.9;
int i = d; // bad: narrowing: i becomes 7 int i = d; // bad: narrowing: i becomes 7
i = (int)d; // bad: we're going to claim this is still not explicit enough i = (int) d; // bad: we're going to claim this is still not explicit enough
void f(int x, long y, double d) void f(int x, long y, double d)
{ {
@ -10507,11 +10509,11 @@ Such examples are often handled as well or better using `mutable` or an indirect
Consider keeping previously computed results around for a costly operation: Consider keeping previously computed results around for a costly operation:
int compute(int x); // compute a value for x; assume this to be costly int compute(int x); // compute a value for x; assume this to be costly
class Cache { // some type implementing a cache for an int->int operation class Cache { // some type implementing a cache for an int->int operation
public: public:
pair<bool,int> find(int x) const; // is there a value for x? pair<bool, int> find(int x) const; // is there a value for x?
void set(int x, int v); // make y the value for x void set(int x, int v); // make y the value for x
// ... // ...
private: private:
@ -10523,9 +10525,9 @@ Consider keeping previously computed results around for a costly operation:
int get_val(int x) int get_val(int x)
{ {
auto p = cache.find(x); auto p = cache.find(x);
if (p.first) return p.second; if (p.first) return p.second;
int val = compute(x); int val = compute(x);
cache.set(x,val); // insert value for x cache.set(x, val); // insert value for x
return val; return val;
} }
// ... // ...
@ -10541,10 +10543,10 @@ To do this we still need to mutate `cache`, so people sometimes resort to a `con
int get_val(int x) const int get_val(int x) const
{ {
auto p = cache.find(x); auto p = cache.find(x);
if (p.first) return p.second; if (p.first) return p.second;
int val = compute(x); int val = compute(x);
const_cast<Cache&>(cache).set(x,val); // ugly const_cast<Cache&>(cache).set(x, val); // ugly
return val; return val;
} }
// ... // ...
private: private:
@ -10559,10 +10561,10 @@ State that `cache` is mutable even for a `const` object:
int get_val(int x) const int get_val(int x) const
{ {
auto p = cache.find(x); auto p = cache.find(x);
if (p.first) return p.second; if (p.first) return p.second;
int val = compute(x); int val = compute(x);
cache.set(x,val); cache.set(x, val);
return val; return val;
} }
// ... // ...
private: private:
@ -10576,10 +10578,10 @@ An alternative solution would to store a pointer to the `cache`:
int get_val(int x) const int get_val(int x) const
{ {
auto p = cache->find(x); auto p = cache->find(x);
if (p.first) return p.second; if (p.first) return p.second;
int val = compute(x); int val = compute(x);
cache->set(x, val); cache->set(x, val);
return val; return val;
} }
// ... // ...
private: private:
@ -10906,22 +10908,22 @@ Unsigned arithmetic can yield surprising results if you are not expecting it.
This is even more true for mixed signed and unsigned arithmetic. This is even more true for mixed signed and unsigned arithmetic.
template<typename T, typename T2> template<typename T, typename T2>
T subtract(T x, T2 y) T subtract(T x, T2 y)
{ {
return x-y; return x-y;
} }
void test() void test()
{ {
int s = 5; int s = 5;
unsigned int us = 5; unsigned int us = 5;
cout << subtract(s, 7) << '\n'; // -2 cout << subtract(s, 7) << '\n'; // -2
cout << subtract(us, 7u) << '\n'; // 4294967294 cout << subtract(us, 7u) << '\n'; // 4294967294
cout << subtract(s, 7u) << '\n'; // -2 cout << subtract(s, 7u) << '\n'; // -2
cout << subtract(us, 7) << '\n'; // 4294967294 cout << subtract(us, 7) << '\n'; // 4294967294
cout << subtract(s, us+2) << '\n'; // -2 cout << subtract(s, us+2) << '\n'; // -2
cout << subtract(us, s+2) << '\n'; // 4294967294 cout << subtract(us, s+2) << '\n'; // 4294967294
} }
Here we have been very explicit about what's happening, Here we have been very explicit about what's happening,
but if you had see `us-(s+2)` or `s+=2; ... us-s` would you reliably have suspected that the result would print as `4294967294`? but if you had see `us-(s+2)` or `s+=2; ... us-s` would you reliably have suspected that the result would print as `4294967294`?
@ -10939,12 +10941,15 @@ The build-in array uses signed types for subscripts.
This makes surprises (and bugs) inevitable. This makes surprises (and bugs) inevitable.
int a[10]; int a[10];
for (int i=0; i<10; ++i) a[i]=i; for (int i=0; i < 10; ++i) a[i]=i;
vector<int> v(10); vector<int> v(10);
for (int i=0; v.size()<10; ++i) v[i]=i; // compares signed to unsigned; some compilers warn // compares signed to unsigned; some compilers warn
for (int i=0; v.size() < 10; ++i) v[i]=i;
int a2[-2]; // error: negative size int a2[-2]; // error: negative size
vector<int> v2(-2); // OK, but the number of ints (4294967294) is so large that we should get an exception
// OK, but the number of ints (4294967294) is so large that we should get an exception
vector<int> v2(-2);
##### Enforcement ##### Enforcement
@ -11190,7 +11195,7 @@ Because a design that ignore the possibility of later improvement is hard to cha
From the C (and C++) standard: From the C (and C++) standard:
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*)); void qsort (void* base, size_t num, size_t size, int (*compar)(const void*, const void*));
When did you even want to sort memory? When did you even want to sort memory?
Really, we sort sequences of elements, typically stored in containers. Really, we sort sequences of elements, typically stored in containers.
@ -11200,7 +11205,10 @@ This implies added work for the programmer, is error prone, and deprives the com
double data[100]; double data[100];
// ... fill a ... // ... fill a ...
qsort(data,100,sizeof(double),compare_doubles); // 100 chunks of memory of sizeof(double) starting at address data using the order defined by compare_doubles
// 100 chunks of memory of sizeof(double) starting at
// address data using the order defined by compare_doubles
qsort(data, 100, sizeof(double), compare_doubles);
From the point of view of interface design is that `qsort` throws away useful information. From the point of view of interface design is that `qsort` throws away useful information.
@ -11209,13 +11217,15 @@ We can do better (in C++98)
template<typename Iter> template<typename Iter>
void sort(Iter b, Iter e); // sort [b:e) void sort(Iter b, Iter e); // sort [b:e)
sort(data,data+100); sort(data, data + 100);
Here, we use the compiler's knowledge about the size of the array, the type of elements, and how to compare `double`s. Here, we use the compiler's knowledge about the size of the array, the type of elements, and how to compare `double`s.
With C++11 plus [concepts](#???), we can do better still With C++11 plus [concepts](#???), we can do better still
void sort(Sortable& c); // Sortable specifies that c must be a random-access sequence of elements comparable with < // Sortable specifies that c must be a
// random-access sequence of elements comparable with <
void sort(Sortable& c);
sort(c); sort(c);
@ -11224,7 +11234,8 @@ In this, the `sort` interfaces shown here still have a weakness:
They implicitly rely on the element type having less-than (`<`) defined. They implicitly rely on the element type having less-than (`<`) defined.
To complete the interface, we need a second version that accepts a comparison criteria: To complete the interface, we need a second version that accepts a comparison criteria:
void sort(Sortable& c, Predicate<Value_type<Sortable>> p); // compare elements of c using p // compare elements of c using p
void sort(Sortable& c, Predicate<Value_type<Sortable>> p);
The standard-library specification of `sort` offers those two versions, The standard-library specification of `sort` offers those two versions,
but the semantics is expressed in English rather than code using concepts. but the semantics is expressed in English rather than code using concepts.
@ -11267,7 +11278,7 @@ Don't let bad designs "bleed into" your code.
Consider: Consider:
template <class ForwardIterator, class T> template <class ForwardIterator, class T>
bool binary_search (ForwardIterator first, ForwardIterator last, const T& val); bool binary_search(ForwardIterator first, ForwardIterator last, const T& val);
`binary_search(begin(c),end(c),7)` will tell you whether `7` is in `c` or not. `binary_search(begin(c),end(c),7)` will tell you whether `7` is in `c` or not.
However, it will not tell you where that `7` is or whether there are more than one `7`. However, it will not tell you where that `7` is or whether there are more than one `7`.
@ -11283,13 +11294,13 @@ needed information back to the caller. Therefore, the standard library also offe
However, `lower_bound` still doesn't return enough information for all uses, so the standard library also offers However, `lower_bound` still doesn't return enough information for all uses, so the standard library also offers
template <class ForwardIterator, class T> template <class ForwardIterator, class T>
pair<ForwardIterator,ForwardIterator> pair<ForwardIterator, ForwardIterator>
equal_range (ForwardIterator first, ForwardIterator last, const T& val); equal_range(ForwardIterator first, ForwardIterator last, const T& val);
`equal_range` returns a `pair` of iterators specifying the first and one beyond last match. `equal_range` returns a `pair` of iterators specifying the first and one beyond last match.
auto r = equal_range(begin(c),end(c),7); auto r = equal_range(begin(c), end(c),7);
for (auto p = r.first(); p!=r.second(), ++p) for (auto p = r.first(); p != r.second(), ++p)
cout << *p << '\n'; cout << *p << '\n';
Obviously, these three interfaces are implemented by the same basic code. Obviously, these three interfaces are implemented by the same basic code.
@ -12069,7 +12080,7 @@ A `thread` that has not been `detach()`ed when it is destroyed terminates the pr
##### Enforcement ##### Enforcement
* Flag `join's for `raii_thread`s ??? * Flag `join`s for `raii_thread`s ???
* Flag `detach`s for `detached_thread`s * Flag `detach`s for `detached_thread`s
@ -13957,7 +13968,7 @@ It also avoids brittle or inefficient workarounds. Convention: That's the way th
}; };
Container c(10, sizeof(double)); Container c(10, sizeof(double));
((double*)c.elem)[] = 9.9; ((double*) c.elem)[] = 9.9;
This doesn't directly express the intent of the programmer and hides the structure of the program from the type system and optimizer. This doesn't directly express the intent of the programmer and hides the structure of the program from the type system and optimizer.
@ -15458,7 +15469,7 @@ Variadic templates is the most general mechanism for that, and is both efficient
##### Enforcement ##### Enforcement
* Flag uses of `va_arg` in user code. * Flag uses of `va_arg` in user code.
### <a name="Rt-variadic-pass"></a>T.101: ??? How to pass arguments to a variadic template ??? ### <a name="Rt-variadic-pass"></a>T.101: ??? How to pass arguments to a variadic template ???
@ -15612,7 +15623,7 @@ Often a `constexpr` function implies less compile-time overhead than alternative
##### Enforcement ##### Enforcement
* Flag template metaprograms yielding a value. These should be replaced with `constexpr` functions. * Flag template metaprograms yielding a value. These should be replaced with `constexpr` functions.
### <a name="Rt-std-tmp"></a>T.124: Prefer to use standard-library TMP facilities ### <a name="Rt-std-tmp"></a>T.124: Prefer to use standard-library TMP facilities
@ -15762,7 +15773,7 @@ Use `!=` instead of `<` to compare iterators; `!=` works for more objects becaus
// ... // ...
} }
Of course, range-for is better still where it does what you want. Of course, range-`for` is better still where it does what you want.
##### Example ##### Example
@ -15891,7 +15902,7 @@ That subset can be compiled with both C and C++ compilers, and when compiled as
int* p1 = malloc(10 * sizeof(int)); // not C++ int* p1 = malloc(10 * sizeof(int)); // not C++
int* p2 = static_cast<int*>(malloc(10 * sizeof(int))); // not C, C-style C++ int* p2 = static_cast<int*>(malloc(10 * sizeof(int))); // not C, C-style C++
int* p3 = new int[10]; // not C int* p3 = new int[10]; // not C
int* p4 = (int*)malloc(10 * sizeof(int)); // both C and C++ int* p4 = (int*) malloc(10 * sizeof(int)); // both C and C++
##### Enforcement ##### Enforcement
@ -16409,7 +16420,7 @@ This slowdown can be significant compared to `printf`-style output.
##### Example ##### Example
cout << "Hello, World!" << endl; // two output operations and a flush cout << "Hello, World!" << endl; // two output operations and a flush
cout << "hello, World!\n"; // one output operation and no flush cout << "Hello, World!\n"; // one output operation and no flush
##### Note ##### Note
@ -16546,9 +16557,9 @@ In particular, the single-return rule makes it harder to concentrate error check
// requires Number<T> // requires Number<T>
string sign(T x) string sign(T x)
{ {
if (x<0) if (x < 0)
return "negative"; return "negative";
else if (x>0) else if (x > 0)
return "positive"; return "positive";
return "zero"; return "zero";
} }
@ -16560,12 +16571,12 @@ to use a single return only we would have to do something like
string sign(T x) // bad string sign(T x) // bad
{ {
string res; string res;
if (x<0) if (x < 0)
res = "negative"; res = "negative";
else if (x>0) else if (x > 0)
res = "positive"; res = "positive";
else else
res ="zero"; res = "zero";
return res; return res;
} }
@ -16577,7 +16588,7 @@ Of course many simple functions will naturally have just one `return` because of
int index(const char* p) int index(const char* p)
{ {
if (p==nullptr) return -1; // error indicator: alternatively `throw nullptr_error{}` if (p == nullptr) return -1; // error indicator: alternatively "throw nullptr_error{}"
// ... do a lookup to find the index for p // ... do a lookup to find the index for p
return i; return i;
} }
@ -16587,7 +16598,7 @@ If we applied the rule, we'd get something like
int index2(const char* p) int index2(const char* p)
{ {
int i; int i;
if (p==nullptr) if (p == nullptr)
i = -1; // error indicator i = -1; // error indicator
else { else {
// ... do a lookup to find the index for p // ... do a lookup to find the index for p
@ -16715,9 +16726,9 @@ This technique is a pre-exception technique for RAII-like resource and error han
void do_something(int n) void do_something(int n)
{ {
if (n<100) goto exit; if (n < 100) goto exit;
// ... // ...
int* p = (int*)malloc(n); int* p = (int*) malloc(n);
// ... // ...
if (some_ error) goto_exit; if (some_ error) goto_exit;
// ... // ...