Skip to content

Commit 4efa0fb

Browse files
authored
Merge pull request #666 from tkruse/dodgy-code
Minor dodgy code issues
2 parents bc9d0b0 + ced78ff commit 4efa0fb

File tree

1 file changed

+35
-36
lines changed

1 file changed

+35
-36
lines changed

CppCoreGuidelines.md

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3524,7 +3524,6 @@ but:
35243524
int y;
35253525
Month m;
35263526
char d; // day
3527-
Date(int yy, Month mm, char dd);
35283527
};
35293528

35303529
##### Note
@@ -4169,7 +4168,7 @@ Note that if you define a destructor, you must define or delete [all default ope
41694168
~Smart_ptr2() { delete p; } // p is an owner!
41704169
};
41714170

4172-
void use(Smart_ptr<int> p1)
4171+
void use(Smart_ptr2<int> p1)
41734172
{
41744173
auto p2 = p1; // error: double deletion
41754174
}
@@ -4279,7 +4278,7 @@ See [this in the Discussion section](#Sd-dtor).
42794278
##### Example, bad
42804279

42814280
struct Base { // BAD: no virtual destructor
4282-
virtual f();
4281+
virtual void f();
42834282
};
42844283

42854284
struct D : Base {
@@ -4440,8 +4439,8 @@ The C++11 initializer list rule eliminates the need for many constructors. For e
44404439
Rec2(const string& ss, int ii = 0) :s{ss}, i{ii} {} // redundant
44414440
};
44424441

4443-
Rec r1 {"Foo", 7};
4444-
Rec r2 {"Bar"};
4442+
Rec2 r1 {"Foo", 7};
4443+
Rec2 r2 {"Bar"};
44454444

44464445
The `Rec2` constructor is redundant.
44474446
Also, the default for `int` would be better done as a [member initializer](#Rc-in-class-initializer).
@@ -4532,7 +4531,7 @@ Leaving behind an invalid object is asking for trouble.
45324531
// ...
45334532
}
45344533

4535-
void is_valid() { return valid; }
4534+
bool is_valid() { return valid; }
45364535
void read(); // read from f
45374536
// ...
45384537
};
@@ -4542,7 +4541,7 @@ Leaving behind an invalid object is asking for trouble.
45424541
X3 file {"Heraclides"};
45434542
file.read(); // crash or bad read!
45444543
// ...
4545-
if (is_valid()) {
4544+
if (file.is_valid()) {
45464545
file.read();
45474546
// ...
45484547
}
@@ -4619,7 +4618,7 @@ A class with members that all have default constructors implicitly gets a defaul
46194618

46204619
struct X {
46214620
string s;
4622-
vector v;
4621+
vector<int> v;
46234622
};
46244623

46254624
X x; // means X{{}, {}}; that is the empty string and the empty vector
@@ -4945,7 +4944,7 @@ To avoid repetition and accidental differences.
49454944
int y;
49464945
public:
49474946
Date(int ii, Month mm, year yy)
4948-
:i{ii}, m{mm} y{yy}
4947+
:i{ii}, m{mm}, y{yy}
49494948
{ if (!valid(i, m, y)) throw Bad_date{}; }
49504949

49514950
Date(int ii, Month mm)
@@ -4964,7 +4963,7 @@ The common action gets tedious to write and may accidentally not be common.
49644963
int y;
49654964
public:
49664965
Date2(int ii, Month mm, year yy)
4967-
:i{ii}, m{mm} y{yy}
4966+
:i{ii}, m{mm}, y{yy}
49684967
{ if (!valid(i, m, y)) throw Bad_date{}; }
49694968

49704969
Date2(int ii, Month mm)
@@ -5476,9 +5475,9 @@ Because we defined the destructor, we must define the copy and move operations.
54765475
~Tracer2() { cerr << "exiting " << message << '\n'; }
54775476

54785477
Tracer2(const Tracer2& a) : message{a.message} {}
5479-
Tracer2& operator=(const Tracer2& a) { message = a.message; }
5478+
Tracer2& operator=(const Tracer2& a) { message = a.message; return *this; }
54805479
Tracer2(Tracer2&& a) :message{a.message} {}
5481-
Tracer2& operator=(Tracer2&& a) { message = a.message; }
5480+
Tracer2& operator=(Tracer2&& a) { message = a.message; return *this; }
54825481
};
54835482

54845483
Writing out the bodies of the copy and move operations is verbose, tedious, and error-prone. A compiler does it better.
@@ -6231,7 +6230,7 @@ but bear with us because this is just a simple example of a technique aimed at m
62316230

62326231

62336232
class Impl::Circle : public Circle, public Impl::Shape { // implementation
6234-
publc:
6233+
public:
62356234
// constructors, destructor
62366235

62376236
int radius() { /* ... */ }
@@ -6246,7 +6245,7 @@ And we could extend the hierarchies by adding a Smiley class (:-)):
62466245
};
62476246

62486247
class Impl::Smiley : Public Smiley, public Impl::Circle { // implementation
6249-
publc:
6248+
public:
62506249
// constructors, destructor
62516250
// ...
62526251
}
@@ -7105,7 +7104,7 @@ Avoiding inconsistent definition in different namespaces
71057104
bool operator==(S, S); // OK: in the same namespace as S, and even next to S
71067105
S s;
71077106

7108-
bool s == s;
7107+
bool x = (s == s);
71097108

71107109
This is what a default `==` would do, if we had such defaults.
71117110

@@ -7118,7 +7117,7 @@ This is what a default `==` would do, if we had such defaults.
71187117

71197118
N::S s;
71207119

7121-
bool s == s; // finds N::operator==() by ADL
7120+
bool x = (s == s); // finds N::operator==() by ADL
71227121

71237122
##### Example, bad
71247123

@@ -7456,7 +7455,7 @@ If you can't name an enumeration, the values are not related
74567455

74577456
##### Example, bad
74587457

7459-
enum { red = 0xFF0000, scale = 4, signed = 1 };
7458+
enum { red = 0xFF0000, scale = 4, is_signed = 1 };
74607459

74617460
Such code is not uncommon in code written before there were convenient alternative ways of specifying integer constants.
74627461

@@ -7466,7 +7465,7 @@ Use `constexpr` values instead. For example:
74667465

74677466
constexpr int red = 0xFF0000;
74687467
constexpr short scale = 4;
7469-
constexpr bool signed = true;
7468+
constexpr bool is_signed = true;
74707469

74717470
##### Enforcement
74727471

@@ -7519,8 +7518,8 @@ The default gives a consecutive set of values that is good for `switch`-statemen
75197518
##### Example
75207519

75217520
enum class Col1 { red, yellow, blue };
7522-
enum class Col2 { red = 1, red = 2, blue = 2 }; // typo
7523-
enum class Month { jan = 1, feb, mar, apr, mar, jun,
7521+
enum class Col2 { red = 1, yellow = 2, blue = 2 }; // typo
7522+
enum class Month { jan = 1, feb, mar, apr, may, jun,
75247523
jul, august, sep, oct, nov, dec }; // starting with 1 is conventional
75257524
enum class Base_flag { dec = 1, oct = dec << 1, hex = dec << 2 }; // set of bits
75267525

@@ -8533,7 +8532,7 @@ The more traditional and lower-level near-equivalent is longer, messier, harder
85338532
is.read(s, maxstring);
85348533
res[elemcount++] = s;
85358534
}
8536-
nread = elemcount;
8535+
nread = &elemcount;
85378536
return res;
85388537
}
85398538

@@ -11114,7 +11113,7 @@ Avoids nasty errors from unreleased locks.
1111411113

1111511114
Sooner or later, someone will forget the `mtx.unlock()`, place a `return` in the `... do stuff ...`, throw an exception, or something.
1111611115

11117-
mutex mtx;
11116+
mutex mtx;
1111811117

1111911118
void do_stuff()
1112011119
{
@@ -11363,7 +11362,7 @@ The plain `thread`s should be assumed to use the full generality of `std::thread
1136311362

1136411363
void use(int n)
1136511364
{
11366-
thread t { thricky, this, n };
11365+
thread t { tricky, this, n };
1136711366
// ...
1136811367
// ... should I join here? ...
1136911368
}
@@ -11923,9 +11922,9 @@ Double-checked locking is easy to mess up.
1192311922

1192411923
atomic<bool> x_init;
1192511924

11926-
if (!x_init.load(memory_order_acquire) {
11925+
if (!x_init.load(memory_order_acquire)) {
1192711926
lock_guard<mutex> lck(x_mutex);
11928-
if (!x_init.load(memory_order_relaxed) {
11927+
if (!x_init.load(memory_order_relaxed)) {
1192911928
// ... initialize x ...
1193011929
x_init.store(true, memory_order_release);
1193111930
}
@@ -12137,7 +12136,7 @@ C++ implementations tend to be optimized based on the assumption that exceptions
1213712136

1213812137
##### Example, don't
1213912138

12140-
// don't: exception not used for error handling
12139+
// don't: exception not used for error handling
1214112140
int find_index(vector<string>& vec, const string& x)
1214212141
{
1214312142
try {
@@ -12181,7 +12180,7 @@ Not all member functions can be called.
1218112180
##### Example
1218212181

1218312182
class vector { // very simplified vector of doubles
12184-
// if elem!=nullptr then elem points to sz doubles
12183+
// if elem != nullptr then elem points to sz doubles
1218512184
public:
1218612185
vector() : elem{nullptr}, sz{0}{}
1218712186
vector(int s) : elem{new double}, sz{s} { /* initialize elements */ }
@@ -12192,7 +12191,7 @@ Not all member functions can be called.
1219212191
private:
1219312192
owner<double*> elem;
1219412193
int sz;
12195-
}
12194+
};
1219612195

1219712196
The class invariant - here stated as a comment - is established by the constructors.
1219812197
`new` throws if it cannot allocate the required memory.
@@ -12294,7 +12293,7 @@ One strategy is to add a `valid()` operation to every resource handle:
1229412293
// handle error or exit
1229512294
}
1229612295

12297-
Ifstream fs("foo"); // not std::ifstream: valid() added
12296+
ifstream fs("foo"); // not std::ifstream: valid() added
1229812297
if (!fs.valid()) {
1229912298
// handle error or exit
1230012299
}
@@ -12370,7 +12369,7 @@ That would be a leak.
1237012369
void leak(int x) // don't: may leak
1237112370
{
1237212371
auto p = new int{7};
12373-
if (x < 0) throw Get_me_out_of_here{} // may leak *p
12372+
if (x < 0) throw Get_me_out_of_here{}; // may leak *p
1237412373
// ...
1237512374
delete p; // we may never get here
1237612375
}
@@ -12387,7 +12386,7 @@ One way of avoiding such problems is to use resource handles consistently:
1238712386

1238812387
Another solution (often better) would be to use a local variable to eliminate explicit use of pointers:
1238912388

12390-
void no_leak(_simplified(int x)
12389+
void no_leak_simplified(int x)
1239112390
{
1239212391
vector<int> v(7);
1239312392
// ...
@@ -12731,7 +12730,7 @@ In such cases, "crashing" is simply leaving error handling to the next level of
1273112730

1273212731
Most programs cannot handle memory exhaustion gracefully anyway. This is roughly equivalent to
1273312732

12734-
void f(Int n)
12733+
void f(int n)
1273512734
{
1273612735
// ...
1273712736
p = new X[n]; // throw if memory is exhausted (by default, terminate)
@@ -13054,8 +13053,8 @@ Better performance, better compile-time checking, guaranteed compile-time evalua
1305413053
##### Example
1305513054

1305613055
double x = f(2); // possible run-time evaluation
13057-
const double x = f(2); // possible run-time evaluation
13058-
constexpr double y = f(2); // error unless f(2) can be evaluated at compile time
13056+
const double y = f(2); // possible run-time evaluation
13057+
constexpr double z = f(2); // error unless f(2) can be evaluated at compile time
1305913058

1306013059
##### Note
1306113060

@@ -16189,7 +16188,7 @@ Note that a C-style `(T)expression` cast means to perform the first of the follo
1618916188
##### Example, bad
1619016189

1619116190
std::string s = "hello world";
16192-
double* p = (double*)(&s); // BAD
16191+
double* p0 = (double*)(&s); // BAD
1619316192

1619416193
class base { public: virtual ~base() = 0; };
1619516194

@@ -16202,7 +16201,7 @@ Note that a C-style `(T)expression` cast means to perform the first of the follo
1620216201
};
1620316202

1620416203
derived1 d1;
16205-
base* p = &d1; // ok, implicit conversion to pointer to base is fine
16204+
base* p1 = &d1; // ok, implicit conversion to pointer to base is fine
1620616205

1620716206
// BAD, tries to treat d1 as a derived2, which it is not
1620816207
derived2* p2 = (derived2*)(p);

0 commit comments

Comments
 (0)