Fix code that does not compile

This commit is contained in:
Thibault Kruse 2015-09-29 11:37:14 +02:00
parent a4c8444032
commit fca180e978

View File

@ -718,23 +718,23 @@ The date is validated twice (by the `Date` constructor) and passed as a characte
There are cases where checking early is dumb because you may not ever need the value, 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. or may only need part of the value that is more easily checked than the whole.
class Jet { // Physics says: e*e < x*x + y*y + z*z class Jet { // Physics says: e*e < x*x + y*y + z*z
float fx, fy, fz, fe; float fx, fy, fz, fe;
public: public:
Jet(float x, float y, float z, float e) Jet(float x, float y, float z, float e)
:fx(x), fy(y), fz(z), fe(e) :fx(x), fy(y), fz(z), fe(e)
{ {
// Should I check the here that the values are physically meaningful? // Should I check the here that the values are physically meaningful?
} }
float m() const float m() const
{ {
// Should I handle the degenerate case here? // Should I handle the degenerate case here?
return sqrt(x*x + y*y + z*z - e*e); return sqrt(x*x + y*y + z*z - e*e);
} }
??? ???
}; };
The physical law for a jet (`e*e < x*x + y*y + z*z`) is not an invariant because the possibility of measurement errors. The physical law for a jet (`e*e < x*x + y*y + z*z`) is not an invariant because the possibility of measurement errors.
@ -2585,8 +2585,8 @@ Subsections:
**Example**: **Example**:
void draw(int x, int y, int x2, int y2); // BAD: unnecessary implicit relationships void draw(int x, int y, int x2, int y2); // BAD: unnecessary implicit relationships
void draw(Point from, Point to) // better void draw(Point from, Point to); // better
**Note**: A simple class without virtual functions implies no space or time overhead. **Note**: A simple class without virtual functions implies no space or time overhead.
@ -3024,7 +3024,7 @@ The whole purpose of `final_action` is to get a piece of code (usually a lambda)
string s; string s;
int i; int i;
vector<int> vi; vector<int> vi;
} };
The default destructor does it better, more efficiently, and can't get it wrong. The default destructor does it better, more efficiently, and can't get it wrong.
@ -3353,7 +3353,7 @@ It is often a good idea to express the invariant as an `Ensure` on the construct
struct Rec2{ struct Rec2{
string s; string s;
int i; int i;
Rec2(const string& ss, int ii = 0} :s{ss}, i{ii} {} // redundant Rec2(const string& ss, int ii = 0) :s{ss}, i{ii} {} // redundant
}; };
Rec r1 {"Foo", 7}; Rec r1 {"Foo", 7};
@ -3413,7 +3413,7 @@ The idiom of having constructors acquire resources and destructors release them
// ... // ...
public: public:
X2(const string& name) X2(const string& name)
:f{fopen(name.c_str(), "r"} :f{fopen(name.c_str(), "r")}
{ {
if (f==nullptr) throw runtime_error{"could not open" + name}; if (f==nullptr) throw runtime_error{"could not open" + name};
// ... // ...
@ -3438,7 +3438,7 @@ The idiom of having constructors acquire resources and destructors release them
// ... // ...
public: public:
X3(const string& name) X3(const string& name)
:f{fopen(name.c_str(), "r"}, valid{false} :f{fopen(name.c_str(), "r")}, valid{false}
{ {
if (f) valid=true; if (f) valid=true;
// ... // ...
@ -3451,7 +3451,7 @@ The idiom of having constructors acquire resources and destructors release them
void f() void f()
{ {
X3 file {Heraclides"}; X3 file {"Heraclides"};
file.read(); // crash or bad read! file.read(); // crash or bad read!
// ... // ...
if (is_valid()()) { if (is_valid()()) {
@ -3881,6 +3881,7 @@ Types can be defined to move for logical as well as performance reasons.
T* elem; T* elem;
int sz; int sz;
}; };
}
Vector& Vector::operator=(const Vector& a) Vector& Vector::operator=(const Vector& a)
{ {
@ -4049,6 +4050,7 @@ Consider:
**Example**: **Example**:
template<typename T>
class X { // OK: value semantics class X { // OK: value semantics
public: public:
X(); X();
@ -4134,6 +4136,7 @@ A non-throwing move will be used more efficiently by standard-library and langua
**Example**: **Example**:
template<typename T>
class Vector { class Vector {
// ... // ...
Vector(Vector&& a) noexcept :elem{a.elem}, sz{a.sz} { a.sz=0; a.elem=nullptr; } Vector(Vector&& a) noexcept :elem{a.elem}, sz{a.sz} { a.sz=0; a.elem=nullptr; }
@ -4148,6 +4151,7 @@ These copy operations do not throw.
**Example, bad**: **Example, bad**:
template<typename T>
class Vector2 { class Vector2 {
// ... // ...
Vector2(Vector2&& a) { *this = a; } // just use the copy Vector2(Vector2&& a) { *this = a; } // just use the copy
@ -6414,8 +6418,8 @@ or better using concepts
or or
double scalbn( // better: x*pow(FLT_RADIX, n); FLT_RADIX is usually 2 double scalbn( // better: x*pow(FLT_RADIX, n); FLT_RADIX is usually 2
double x; // base value double x, // base value
int n; // exponent int n // exponent
); );
or or
@ -6439,7 +6443,7 @@ or
auto s = v.size(); auto s = v.size();
auto h = t.future(); auto h = t.future();
auto q = new int[s]; auto q = new int[s];
auto f = [](int x){ return x+10; } auto f = [](int x){ return x+10; };
In each case, we save writing a longish, hard-to-remember type that the compiler already knows but a programmer could get wrong. In each case, we save writing a longish, hard-to-remember type that the compiler already knows but a programmer could get wrong.
@ -8066,7 +8070,7 @@ One strategy is to add a `valid()` operation to every resource handle:
void f() void f()
{ {
Vector<string> vs(100); // not std::vector: valid() added vector<string> vs(100); // not std::vector: valid() added
if (!vs.valid()) { if (!vs.valid()) {
// handle error or exit // handle error or exit
} }
@ -8606,7 +8610,7 @@ It also avoids brittle or inefficient workarounds. Convention: That's the way th
int sz; int sz;
}; };
Vector<double> v(10); vector<double> v(10);
v[7] = 9.9; v[7] = 9.9;
**Example, bad**: **Example, bad**:
@ -9441,14 +9445,14 @@ The two language mechanisms can be use effectively in combination, but a few des
// ... // ...
}; };
Vector<int> vi; vector<int> vi;
Vector<string> vs; vector<string> vs;
It is probably a dumb idea to define a `sort` as a member function of a container, It is probably a dumb idea to define a `sort` as a member function of a container,
but it is not unheard of and it makes a good example of what not to do. but it is not unheard of and it makes a good example of what not to do.
Given this, the compiler cannot know if `Vector<int>::sort()` is called, so it must generate code for it. Given this, the compiler cannot know if `vector<int>::sort()` is called, so it must generate code for it.
Similar for `Vector<string>::sort()`. Similar for `vector<string>::sort()`.
Unless those two functions are called that's code bloat. Unless those two functions are called that's code bloat.
Imagine what this would do to a class hierarchy with dozens of member functions and dozens of derived classes with many instantiations. Imagine what this would do to a class hierarchy with dozens of member functions and dozens of derived classes with many instantiations.
@ -10798,8 +10802,8 @@ Issue a diagnostic for any indexing expression on an expression or variable of a
void f(int i, int j) void f(int i, int j)
{ {
a[i + j] = 12; // BAD, could be rewritten as... a[i + j] = 12; // BAD, could be rewritten as...
at(a, i + j) = 12; // OK - bounds-checked at(a, i + j) = 12; // OK - bounds-checked
} }
@ -11966,7 +11970,7 @@ Now `Named` has a default constructor, a destructor, and efficient copy and move
template<typename T> class Vector { template<typename T> class Vector {
public: public:
Vector<std::initializer_list<T>); vector<std::initializer_list<T>>;
// ... // ...
}; };