@ -592,7 +592,7 @@ This example is easily simplified
##### Example
void read(int* p, int n); // read max n integers into *p
int a[100];
read(a, 1000); // bad
@ -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`)isnotaninvariantbecauseofthepossibilityformeasurementerrors.
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,17 +982,17 @@ 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.
Instead, we could use `vector`:
vector<int> v(100);
v.push_back(yet_another)int);
##### Note
@ -1554,7 +1553,7 @@ A facility [structured bindings](http://www.open-std.org/jtc1/sc22/wg21/docs/pap
// ... handle the error or exit ...
}
// ... use val ...
##### Note
@ -1791,7 +1790,7 @@ To really reduce the number of arguments, we need to bundle the arguments into h
Grouping arguments into "bundles" is a general technique to reduce the number of arguments and to increase the opportunities for checking.
Alternatively, we could use concepts (as defined by the ISO TS) to define the notion of three types that must be usable for merging:
Mergeable{In1 In2, Out}
OutputIterator merge(In1 r1, In2 r2, Out result);
@ -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.
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](#Rh-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;
}
// ...
};
@ -6082,7 +6085,7 @@ by making useful operations available for implementers of related new operations
A pure interface class is simply a set of pure virtual functions; see [I.25](#Ri-abstract).
In early OOP (e.g., in the 1980s and 1990s), implementation inheritance and interface inheritance were often mixed
In early OOP (e.g., in the 1980s and 1990s), implementation inheritance and interface inheritance were often mixed
and bad habits die hard.
Even now, mixtures are not uncommon in old code bases and in old-style teaching material.
@ -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; }
@ -6164,7 +6167,7 @@ Note that a pure interface rarely have constructors: there is nothing to constru
class Circle : public Shape {
public:
Circle(Point c, int r, Color c) :cent{c}, rad{r}, col{c} { /* ... */ }
Point center() const override { return cent; }
Color color() const override { 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
@ -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<typenameT> 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`.
@ -14210,7 +14217,7 @@ That is, it is highly visible.
##### Note
This rule should not be necessary, but the committee cannot agree to exclude unconstrained templated from ADL.
This rule should not be necessary, but the committee cannot agree to exclude unconstrained templated from ADL.
Unfortunately this will get many false positives; the standard library violates this widely, by putting many unconstrained templates and types into the single namespace `std`.
@ -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; }
@ -14448,7 +14454,7 @@ This is a simplified version of `std::copy` (ignoring the possibility of non-con
struct pod_tag {};
struct non_pod_tag;
template<classT> struct copy_trait { using tag = non_pod_tag; }; // T is not "plain old data"
template<> struct copy_trait<int> { using tab = pod_tag; }; // int is "plain old data"
@ -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,20 +15020,20 @@ 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]))returnfalse;
if (a.size()!=b.size()) return false;
for (int i=0; i<a.size();++i)if(tolower(a[i])!=tolower(b[i]))returnfalse;
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); }
);
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); }
);
@ -15055,7 +15061,7 @@ That makes the code concise and gives better locality than alternatives.
auto earlyUsersEnd = std::remove_if(users.begin(), users.end(),
[](const User &a) { return a.id > 100; });
##### Exception
@ -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: