style fixes

This commit is contained in:
Thibault Kruse 2016-08-17 18:01:58 +02:00
parent 0785e0b415
commit 4cbbf55bd3

View File

@ -802,7 +802,6 @@ Excess checking can be costly.
There are cases where checking early is dumb because you may not ever need the value, or may only need part of the value that is more easily checked than the whole. Similarly, don't add validity checks that change the asymptotic behavior of your interface (e.g., don't add a `O(n)` check to an interface with an average complexity of `O(1)`).
class Jet { // Physics says: e * e < x * x + y * y + z * z
float x;
float y;
float z;
@ -823,7 +822,7 @@ There are cases where checking early is dumb because you may not ever need the v
???
};
The physical law for a jet (`e*e < x*x + y*y + z*z`) is not an invariant because of the possibility for measurement errors.
The physical law for a jet (`e * e < x * x + y * y + z * z`) is not an invariant because of the possibility for measurement errors.
???
@ -983,10 +982,10 @@ Messy, low-level code breads more such code.
##### Example
int sz = 100;
int* p = (int*) malloc(sizeof(int)*sz);
int* p = (int*) malloc(sizeof(int) * sz);
// ...
if (count==sz)
p = (int*) realloc(p,sizeof(int)*sz*2);
if (count == sz)
p = (int*) realloc(p, sizeof(int) * sz * 2);
// ...
This is low-level, verbose, and error-prone.
@ -2101,7 +2100,6 @@ Consider:
// simpleFunc: takes a value and calculates the expected ASIC output,
// given the two mode flags.
{
double intermediate;
if (flag1 > 0) {
intermediate = func1(val);
@ -2118,9 +2116,10 @@ Consider:
intermediate = func2(intermediate);
}
switch (flag2 / 10) {
case 1: if (flag1 == -1) return finalize(intermediate, 1.171); break;
case 1: if (flag1 == -1) return finalize(intermediate, 1.171);
break;
case 2: return finalize(intermediate, 13.1);
default: ;
default: break;
}
return finalize(intermediate, 0.);
}
@ -2247,7 +2246,7 @@ Specifying `inline` encourages the compiler to do a better job.
##### Example
inline string cat(const string& s, const string& s2) { return s+s2; }
inline string cat(const string& s, const string& s2) { return s + s2; }
**Exception**: Do not put an `inline` function in what is meant to be a stable interface unless you are really sure that it will not change.
An inline function is part of the ABI.
@ -2769,21 +2768,21 @@ It complicates checking and tool support.
void use(int* p, int n, char* s, int* q)
{
p[n-1] = 666; // Bad: we don't know if p points to n elements;
// assume it does not or use span<int>
cout << s; // Bad: we don't know if that s points to a zero-terminated array of char;
// assume it does not or use zstring
delete q; // Bad: we don't know if *q is allocated on the free store;
// assume it does not or use owner
p[n - 1] = 666; // Bad: we don't know if p points to n elements;
// assume it does not or use span<int>
cout << s; // Bad: we don't know if that s points to a zero-terminated array of char;
// assume it does not or use zstring
delete q; // Bad: we don't know if *q is allocated on the free store;
// assume it does not or use owner
}
better
void use2(span<int> p, zstring s, owner<int*> q)
{
p[p.size()-1] = 666; // OK, a range error can be caught
p[p.size() - 1] = 666; // OK, a range error can be caught
cout << s; // OK
delete q; // OK
delete q; // OK
}
##### Note
@ -3257,7 +3256,7 @@ principle of "do as the ints do."
##### Note
Historically there was some guidance to make the assignment operator return `const T&`.
This was primarily to avoid code of the form `(a=b)=c` -- such code is not common enough to warrant violating consistency with standard types.
This was primarily to avoid code of the form `(a = b) = c` -- such code is not common enough to warrant violating consistency with standard types.
##### Example
@ -3338,7 +3337,7 @@ There is not a choice when a set of functions are used to do a semantically equi
##### See also
[Default arguments for virtual functions](#Rf-virtual-default-arg}
[Default arguments for virtual functions](#Rf-virtual-default-arg}
##### Enforcement
@ -3709,7 +3708,7 @@ Flag protected data.
One ideal for a class is to be a regular type.
That means roughly "behaves like an `int`." A concrete type is the simplest kind of class.
A value of regular type can be copied and the result of a copy is an independent object with the same value as the original.
If a concrete type has both `=` and `==`, `a=b` should result in `a == b` being `true`.
If a concrete type has both `=` and `==`, `a = b` should result in `a == b` being `true`.
Concrete classes without assignment and equality can be defined, but they are (and should be) rare.
The C++ built-in types are regular, and so are standard-library classes, such as `string`, `vector`, and `map`.
Concrete types are also often referred to as value types to distinguish them from types uses as part of a hierarchy.
@ -3793,7 +3792,7 @@ Regular types are easier to understand and reason about than types that are not
b2.name = "the other bundle";
if (b1 == b2) error("No!");
In particular, if a concrete type has an assignment also give it an equals operator so that `a=b` implies `a == b`.
In particular, if a concrete type has an assignment also give it an equals operator so that `a = b` implies `a == b`.
##### Enforcement
@ -4247,7 +4246,7 @@ Independently of whether `Handle` owns its `Shape`, we must consider the default
Handle y {*new Triangle{p1, p2, p3}};
x = y; // the default assignment will try *x.s = *y.s
That `x=y` is highly suspect.
That `x = y` is highly suspect.
Assigning a `Triangle` to a `Circle`?
Unless `Shape` has its [copy assignment `=deleted`](#Rc-copy-virtual), only the `Shape` part of `Triangle` is copied into the `Circle`.
@ -5092,7 +5091,7 @@ See [copy constructor vs. `clone()`](#Rc-copy-virtual).
##### Reason
That is the generally assumed semantics. After `x=y`, we should have `x == y`.
That is the generally assumed semantics. After `x = y`, we should have `x == y`.
After a copy `x` and `y` can be independent objects (value semantics, the way non-pointer built-in types and the standard-library types work) or refer to a shared object (pointer semantics, the way pointers work).
##### Example
@ -5163,7 +5162,7 @@ Prefer copy semantics unless you are building a "smart pointer". Value semantics
##### Reason
If `x=x` changes the value of `x`, people will be surprised and bad errors will occur (often including leaks).
If `x = x` changes the value of `x`, people will be surprised and bad errors will occur (often including leaks).
##### Example
@ -5246,7 +5245,7 @@ Equivalent to what is done for [copy-assignment](#Rc-copy-assignment).
##### Reason
That is the generally assumed semantics.
After `y=std::move(x)` the value of `y` should be the value `x` had and `x` should be in a valid state.
After `y = std::move(x)` the value of `y` should be the value `x` had and `x` should be in a valid state.
##### Example
@ -5324,7 +5323,7 @@ The one-in-a-million argument against `if (this == &a) return *this;` tests from
##### Note
There is no know general way of avoiding a `if (this == &a) return *this;` test for a move assignment and still get a correct answer (i.e., after `x=x` the value of `x` is unchanged).
There is no know general way of avoiding a `if (this == &a) return *this;` test for a move assignment and still get a correct answer (i.e., after `x = x` the value of `x` is unchanged).
##### Note
@ -5668,14 +5667,18 @@ Asymmetric treatment of operands is surprising and a source of errors where conv
int number;
};
bool operator==(const X& a, const X& b) noexcept { return a.name == b.name && a.number == b.number; }
bool operator==(const X& a, const X& b) noexcept {
return a.name == b.name && a.number == b.number;
}
##### Example, bad
class B {
string name;
int number;
bool operator==(const B& a) const { return name == a.name && number == a.number; }
bool operator==(const B& a) const {
return name == a.name && number == a.number;
}
// ...
};
@ -6099,7 +6102,7 @@ The importance of keeping the two kinds of inheritance increases
class Shape { // BAD, mixed interface and implementation
public:
Shape();
Shape(Point ce = {0,0}, Color co = none): cent{ce}, col {co} { /* ... */}
Shape(Point ce = {0, 0}, Color co = none): cent{ce}, col {co} { /* ... */}
Point center() const { return cent; }
Color color() const { return col; }
@ -6503,7 +6506,8 @@ Capping an individual virtual function with `final` is error-prone as that `fina
class Widget { /* ... */ };
class My_widget final : public Widget { /* ... */ }; // nobody will ever want to improve My_widget (or so you thought)
// nobody will ever want to improve My_widget (or so you thought)
class My_widget final : public Widget { /* ... */ };
class My_improved_widget : public My_widget { /* ... */ }; // error: can't do that
@ -6876,7 +6880,7 @@ Minimize surprises.
// ...
X& operator=(const X&); // member function defining assignment
friend bool operator==(const X&, const X&); // == needs access to representation
// after a=b we have a==b
// after a = b we have a == b
// ...
};
@ -7409,7 +7413,7 @@ Convenience of use and avoidance of errors.
Day operator++(Day& d)
{
return d==sun?mon:Day{++d};
return d == sun ? mon : Day{++d};
}
Day today = sat;
@ -7458,7 +7462,7 @@ Such code is not uncommon in code written before there were convenient alternati
Use `constexpr` values instead. For example:
constexpr int red = 0x,FF0000;
constexpr int red = 0xFF0000;
constexpr short scale = 4;
constexpr bool signed = true;
@ -7626,8 +7630,8 @@ What is `Port`? A handy wrapper that encapsulates the resource:
operator PortHandle() { return port; }
// port handles can't usually be cloned, so disable copying and assignment if necessary
Port(const Port&) =delete;
Port& operator=(const Port&) =delete;
Port(const Port&) = delete;
Port& operator=(const Port&) = delete;
};
##### Note
@ -8689,7 +8693,8 @@ Here, there is a chance that the reader knows what `trim_tail` means and that th
Argument names of large functions are de facto non-local and should be meaningful:
void complicated_algorithm(vector<Record>& vr, const vector<int>& vi, map<string, int>& out)
// read from events in vr (marking used Records) for the indices in vi placing (name, index) pairs into out
// read from events in vr (marking used Records) for the indices in
// vi placing (name, index) pairs into out
{
// ... 500 lines of code using vr, vi, and out ...
}
@ -9416,13 +9421,15 @@ Requires messy cast-and-macro-laden code to get working right.
#include <cstdarg>
void error(int severity ...) // "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 ...)
{
va_list ap; // a magic type for holding arguments
va_start(ap, severity); // arg startup: "severity" is the first argument of error()
for (;;) {
char* p = va_arg(ap, char*); // treat the next var as a char*; no checking: a cast in disguise
// treat the next var as a char*; no checking: a cast in disguise
char* p = va_arg(ap, char*);
if (p == nullptr) break;
cerr << p << ' ';
}
@ -11139,14 +11146,14 @@ This is asking for deadlock:
Instead, use `lock()`:
// thread 1
lock_guard<mutex> lck1(m1,defer_lock);
lock_guard<mutex> lck2(m2,defer_lock);
lock(lck1,lck2);
lock_guard<mutex> lck1(m1, defer_lock);
lock_guard<mutex> lck2(m2, defer_lock);
lock(lck1, lck2);
// thread 2
lock_guard<mutex> lck2(m2,defer_lock);
lock_guard<mutex> lck1(m1,defer_lock);
lock(lck2,lck1);
lock_guard<mutex> lck2(m2, defer_lock);
lock_guard<mutex> lck1(m1, defer_lock);
lock(lck2, lck1);
Here, the writers of `thread1` and `thread2` are still not agreeing on the order of the `mutex`es, but order no longer matters.
@ -11157,7 +11164,7 @@ In real code, `mutex`es are not always conveniently acquired on consecutive line
I'm really looking forward to be able to write plain
lock_guard lck1(m1,defer_lock);
lock_guard lck1(m1, defer_lock);
and have the `mutex` type deduced.
@ -11231,11 +11238,11 @@ If a `thread` joins, we can safely pass pointers to objects in the scope of the
void some_fct(int* p)
{
int x = 77;
raii_thread t0(f,&x); // OK
raii_thread t1(f,p); // OK
raii_thread t2(f,&glob); // OK
raii_thread t0(f, &x); // OK
raii_thread t1(f, p); // OK
raii_thread t2(f, &glob); // OK
auto q = make_unique<int>(99);
raii_thread t3(f,q.get()); // OK
raii_thread t3(f, q.get()); // OK
// ...
}
@ -11452,12 +11459,12 @@ Defining "small amount" precisely is impossible.
##### Example
string modify1(string);
void modify2(shared_ptr<string);
void modify2(shared_ptr<string>);
void fct(string& s)
{
auto res = async(modify1,string);
async(modify2,&s);
auto res = async(modify1, s);
async(modify2, &s);
}
The call of `modify1` involves copying two `string` values; the call of `modify2` does not.
@ -11528,8 +11535,8 @@ Thread creation is expensive.
void master(istream& is)
{
for (Message m; is>>m; )
run_list.push_back(new thread(worker,m);}
for (Message m; is >> m; )
run_list.push_back(new thread(worker, m));
}
This spawns a `thread` per message, and the `run_list` is presumably managed to destroy those tasks once they are finished.
@ -11546,7 +11553,7 @@ Instead, we could have a set of pre-created worker threads processing the messag
void worker()
{
for (Message m; m=work.get(); ) {
for (Message m; m = work.get(); ) {
// process
}
}
@ -11698,7 +11705,7 @@ An unnamed local objects is a temporary that immediately goes out of scope.
unique_lock<mutex>(m1);
lock_guard<mutex> {m2};
lock(m1,m2);
lock(m1, m2);
This looks innocent enough, but it isn't.
@ -12086,7 +12093,7 @@ The `File_handle` constructor might defined like this:
:f{fopen(name.c_str(), mode.c_str())}
{
if (!f)
throw runtime_error{"File_handle: could not open "S-+ name + " as " + mode"}
throw runtime_error{"File_handle: could not open " + name + " as " + mode};
}
##### Note
@ -12097,7 +12104,7 @@ Examples:
* A precondition that cannot be met
* A constructor that cannot construct an object (failure to establish its class's [invariant](#Rc-struct))
* An out-of-range error (e.g., `v[v.size()] =7`)
* An out-of-range error (e.g., `v[v.size()] = 7`)
* Inability to acquire a resource (e.g., the network is down)
In contrast, termination of an ordinary loop is not exceptional.
@ -12132,7 +12139,7 @@ C++ implementations tend to be optimized based on the assumption that exceptions
int find_index(vector<string>& vec, const string& x)
{
try {
for (int i =0; i < vec.size(); ++i)
for (int i = 0; i < vec.size(); ++i)
if (vec[i] == x) throw i; // found x
} catch (int i) {
return i;
@ -12175,7 +12182,7 @@ Not all member functions can be called.
// if elem!=nullptr then elem points to sz doubles
public:
vector() : elem{nullptr}, sz{0}{}
vctor(int s) : elem{new double},sz{s} { /* initialize elements */ }
vector(int s) : elem{new double}, sz{s} { /* initialize elements */ }
~vector() { delete elem; }
double& operator[](int s) { return elem[s]; }
@ -12874,9 +12881,9 @@ A not uncommon technique is to gather cleanup at the end of the function to avoi
// ...
exit:
if (g1.valid()) cleanup(g1);
if (g1.valid()) cleanup(g2);
return {res, err};
if (g1.valid()) cleanup(g1);
if (g1.valid()) cleanup(g2);
return {res, err};
}
The larger the function, the more tempting this technique becomes.
@ -13772,10 +13779,10 @@ Otherwise they cannot be distinguished automatically by the compiler.
##### Example (using TS concepts)
template<typename I>
concept bool Input_iter = requires (I iter) { ++iter; };
concept bool Input_iter = requires(I iter) { ++iter; };
template<typename I>
concept bool Fwd_iter = Input_iter<I> && requires (I iter) { iter++; }
concept bool Fwd_iter = Input_iter<I> && requires(I iter) { iter++; }
The compiler can determine refinement based on the sets of required operations (here, suffix `++`).
This decreases the burden on implementers of these types since
@ -13907,10 +13914,10 @@ Obviously, it would be better and easier just to use the standard `EqualityCompa
but - just as an example - if you had to define such a concept, prefer:
template<typename T> concept Equality = requires(T a, T b) {
bool == { a==b }
bool == { a!=b }
// axiom { !(a==b)==(a!=b) }
// axiom { a=b; => a==b } // => means "implies"
bool == { a == b }
bool == { a != b }
// axiom { !(a == b) == (a != b) }
// axiom { a = b; => a == b } // => means "implies"
}
as opposed to defining two meaningless concepts `has_equal` and `has_not_equal` just as helpers in the definition of `Equality`.
@ -14353,7 +14360,6 @@ This looks innocent enough, but ???
// requires Regular<T> && Allocator<A>
class List2 {
public:
using iterator = Link<T>*;
iterator first() const { return head; }
@ -15004,7 +15010,7 @@ Documentation, readability, opportunity for reuse.
auto x = find_if(vr.begin(), vr.end(),
[&](Rec& r) {
if (r.name.size() != n.size()) return false; // name to compare to is in n
for (int i=0; i < r.name.size(); ++i)
for (int i = 0; i < r.name.size(); ++i)
if (tolower(r.name[i]) != tolower(n[i])) return false;
return true;
}
@ -15014,12 +15020,12 @@ There is a useful function lurking here (case insensitive string comparison), as
bool compare_insensitive(const string& a, const string& b)
{
if (a.size()!=b.size()) return false;
for (int i=0; i<a.size(); ++i) if (tolower(a[i])!=tolower(b[i])) return false;
if (a.size() != b.size()) return false;
for (int i = 0; i < a.size(); ++i) if (tolower(a[i]) != tolower(b[i])) return false;
return true;
}
auto x = find_if(vr.begin(),vr.end(),
auto x = find_if(vr.begin(), vr.end(),
[&](Rec& r) { compare_insensitive(r.name, n); }
);
@ -15027,7 +15033,7 @@ Or maybe (if you prefer to avoid the implicit name binding to n):
auto cmp_to_n = [&n](const string& a) { return compare_insensitive(a, n); };
auto x = find_if(vr.begin(),vr.end(),
auto x = find_if(vr.begin(), vr.end(),
[](const Rec& r) { return cmp_to_n(r.name); }
);
@ -16511,6 +16517,7 @@ Also, `std::array<>::fill()` or `std::fill()` or even an empty initializer are b
fill(b, 0); // std::fill() + Ranges TS
if ( a == b ) {
// ...
}
}
@ -16755,7 +16762,8 @@ Code says what is done, not what is supposed to be done. Often intent can be sta
##### Example
void stable_sort(Sortable& c)
// sort c in the order determined by <, keep equal elements (as defined by ==) in their original relative order
// sort c in the order determined by <, keep equal elements (as defined by ==) in
// their original relative order
{
// ... quite a few lines of non-trivial code ...
}
@ -16798,9 +16806,9 @@ Readability. Avoidance of "silly mistakes."
Always indenting the statement after `if (...)`, `for (...)`, and `while (...)` is usually a good idea:
if (i<0) error("negative argument");
if (i < 0) error("negative argument");
if (i<0)
if (i < 0)
error("negative argument");
##### Enforcement
@ -16991,7 +16999,7 @@ Too much space makes the text larger and distracts.
#include < map >
int main (int argc, char * argv [ ])
int main(int argc, char * argv [ ])
{
// ...
}
@ -17166,8 +17174,8 @@ It is really easy to overlook a statement when there is more on a line.
##### Example
int x = 7; char* p = 29; // dont
int x = 7; f(x); ++x; // dont
int x = 7; char* p = 29; // don't
int x = 7; f(x); ++x; // don't
##### Enforcement