diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md
index 1a42dea..881e13f 100644
--- a/CppCoreGuidelines.md
+++ b/CppCoreGuidelines.md
@@ -10,7 +10,7 @@ Editors:
This document is a very early draft. It is inkorrekt, incompleat, and pµøoorly formatted.
Had it been an open source (code) project, this would have been release 0.6.
-Copying, use, modification, and creation of derivative works from this project is licensed under an MIT-style license.
+Copying, use, modification, and creation of derivative works from this project is licensed under an MIT-style license.
Contributing to this project requires agreeing to a Contributor License. See the accompanying LICENSE file for details.
We make this project available to "friendly users" to use, copy, modify, and derive from, hoping for constructive input.
@@ -170,7 +170,7 @@ Remember:
### In.0: Don't panic!
-Take the time to understand the implications of a guideline rule on your program.
+Take the time to understand the implications of a guideline rule on your program.
These guidelines are designed according to the "subset of a superset" principle ([Stroustrup,2005](#BS2005)).
They do not simply define a subset of C++ to be used (for reliability, safety, performance, or whatever).
@@ -480,7 +480,7 @@ The last variant makes it clear that we are not interested in the order in which
A programmer should be familiar with
* [The guideline support library](#S-support)
-* [The ISO C++ standard library](#S-stdlib)
+* [The ISO C++ standard library](#S-stdlib)
* Whatever foundation libraries are used for the current project(s)
**Note**: Alternative formulation: Say what should be done, rather than just how it should be done
@@ -593,7 +593,7 @@ Here, a crucial bit of information (the number of elements) has been so thorough
f2(new int[n],m); // bad: the wrong number of elements can be passed to f()
}
-Passing the number of elements as an argument is better (and far more common) that just passing the pointer and relying on some (unstated) convention for knowing or discovering the number of elements. However (as shown), a simple typo can introduce a serious error. The connection between the two arguments of `f2()` is conventional, rather than explicit.
+Passing the number of elements as an argument is better (and far more common) that just passing the pointer and relying on some (unstated) convention for knowing or discovering the number of elements. However (as shown), a simple typo can introduce a serious error. The connection between the two arguments of `f2()` is conventional, rather than explicit.
Also, it is implicit that `f2()` is supposed to `delete` its argument (or did the caller make a second mistake?).
@@ -691,7 +691,7 @@ We could check earlier and improve the code:
const int n = 10;
int a[n] = {};
// ...
- increment2({a,m}); // maybe typo, maybe m<=n is supposed
+ increment2({a,m}); // maybe typo, maybe m<=n is supposed
// ...
}
@@ -731,7 +731,7 @@ The date is validated twice (by the `Date` constructor) and passed as an charact
**Example**: 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.
+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
float fx, fy, fz, fe;
@@ -825,7 +825,7 @@ Alternatively, mark an owner as such using `owner` from [the GSL](#S-support).
if (p==nullptr) throw Nullptr_error{};
int n = strlen(p);
auto buf = new char[n];
- for (int i = 0; i
### I.1: Make interfaces explicit
-**Reason**: Correctness. Assumptions not stated in an interface are easily overlooked and hard to test.
+**Reason**: Correctness. Assumptions not stated in an interface are easily overlooked and hard to test.
**Example, bad**:
Controlling the behavior of a function through a global (namespace scope) variable (a call mode) is implicit and potentially confusing. For example,
@@ -928,14 +928,14 @@ Functions can be template functions and sets of functions can be classes or clas
**Enforcement**:
* (Simple) A function should not make control-flow decisions based on the values of variables declared at namespace scope.
-* (Simple) A function should not write to variables declared at namespace scope.
+* (Simple) A function should not write to variables declared at namespace scope.
### I.2 Avoid global variables
**Reason**: Non-`const` global variables hide dependencies and make the dependencies subject to unpredictable changes.
-**Example**:
+**Example**:
struct Data {
// ... lots of stuff ...
@@ -958,7 +958,7 @@ Who else might modify `data`?
**Note**: The rule against global variables applies to namespace scope variables as well.
**Alternative**: If you use global (more generally namespace scope data) to avoid copying, consider passing the data as an object by const reference.
-Another solution is to define the data as the state of some objects and the operations as member functions.
+Another solution is to define the data as the state of some objects and the operations as member functions.
**Warning**: Beware of data races: if one thread can access nonlocal data (or data passed by reference) while another thread execute the callee, we can have a data race.
Every pointer or reference to mutable data is a potential data race.
@@ -1004,7 +1004,7 @@ and this useful technique is not an exception to the no-singleton rule.
**Enforcement**: Very hard in general
* Look for classes with name that includes `singleton`
-* Look for clases for wich only a single object is created (by counting objects or by examining constructors)
+* Look for clases for which only a single object is created (by counting objects or by examining constructors)
@@ -1033,7 +1033,7 @@ An `int` can carry arbitrary forms of information, so we must guess about the me
Most likely, the first two are an `x`,`y` coordinate pair, but what are the last two?
Comments and parameter names can help, but we could be explicit:
- void draw_rectange(Point top_left, Point bottom_right);
+ void draw_rectange(Point top_left, Point bottom_right);
void draw_rectange(Point top_left, Size height_width);
draw_rectangle(p,Point{10,20}); // two corners
@@ -1047,14 +1047,14 @@ Obviously, we cannot catch all errors through the static type system
**Enforcement**:
-* (Simple) Report the use of void* as a parameter or return type.
+* (Simple) Report the use of void* as a parameter or return type.
* (Hard to do well) Look for member functions with many built-in type arguments
### I.5: State preconditions (if any)
-**Reason**: Arguments have meaning that may constrain their proper use in the callee.
+**Reason**: Arguments have meaning that may constrain their proper use in the callee.
**Example**: Consider
@@ -1079,9 +1079,9 @@ Ideally, that `Expects(x>=0)` should be part of the interface of `sqrt()` but th
That invariant is established by a constructor and must be reestablished upon exit by evenry member function called from outside the class.
We don't need to mentioning it for each member function.
-**Enforcement**: (Not enforceable)
+**Enforcement**: (Not enforceable)
-**See also**: the rules for passing pointers.
+**See also**: the rules for passing pointers.
@@ -1096,7 +1096,7 @@ We don't need to mentioning it for each member function.
Expects(height>0 && width>0); // good
if (height>0 && width>0) my_error(); // obscure
// ...
- }
+ }
**Note**: Preconditions can be stated in many ways, including comments, `if`-statements, and `assert()`. This can make them hard to distinguish from ordinary code, hard to update, hard to manipulate by tools, and may have the wrong semantics (do you always want to abort in debug mode and check nothing in productions runs?).
@@ -1104,13 +1104,13 @@ We don't need to mentioning it for each member function.
**Note**: `Expects()` can also be used to check a condition in the middle of an algorithm.
-**Enforcement**: (Not enforceable) Finding the variety of ways preconditions can be asserted is not feasible. Warning about those that can be easily identfied (assert()) has questionable value in the absence of a language facility.
+**Enforcement**: (Not enforceable) Finding the variety of ways preconditions can be asserted is not feasible. Warning about those that can be easily identfied (assert()) has questionable value in the absence of a language facility.
### I.7: State postconditions
-**Reason**: To detect misunderstandings about the result and possibly catch erroneous implementations.
+**Reason**: To detect misunderstandings about the result and possibly catch erroneous implementations.
**Example; bad**: Consider
@@ -1157,7 +1157,7 @@ There was no postcondition stating that the buffer should be cleared and the opt
void manipulate(Record& r) // don't
{
- m.lock();
+ m.lock();
// ... no m.unlock() ...
}
@@ -1165,7 +1165,7 @@ Here, we "forgot" to state that the `mutex` should be released, so we don't know
void manipulate(Record& r) // better: hold the mutex m while and only while manipulating r
{
- m.lock();
+ m.lock();
// ... no m.unlock() ...
}
@@ -1207,7 +1207,7 @@ Postconditions related only to internal state belongs in the definition/implemen
Ideally, that `Ensured` should be part of the interface that's not easily done. For now, we place it in the definition (function body).
-**Enforcement**: (Not enforceable) Finding the variety of ways postconditions can be asserted is not feasible. Warning about those that can be easily identfied (assert()) has questionable value in the absence of a language facility.
+**Enforcement**: (Not enforceable) Finding the variety of ways postconditions can be asserted is not feasible. Warning about those that can be easily identfied (assert()) has questionable value in the absence of a language facility.
@@ -1217,7 +1217,7 @@ Ideally, that `Ensured` should be part of the interface that's not easily done.
**Example**: Use the ISO Concepts TS style of requirements specification. For example:
- template
+ template
// requires InputIterator && EqualityComparable>,Val>
Iter find(Iter first, Iter last, Val v)
{
@@ -1237,7 +1237,7 @@ Ideally, that `Ensured` should be part of the interface that's not easily done.
**Reason**: It should not be possible to ignore an error because that could leave the system or a computation in an undefined (or unexpected) state.
This is a major source of errors.
-**Example**:
+**Example**:
int printf(const char* ...); // bad: return negative number if output fails
@@ -1361,8 +1361,8 @@ Note: `length()` is, of course, `std::strlen()` in disguise.
**Enforcement**:
-* (Simple) ((Foundation)) If a function checks a pointer parameter against `nullptr` before access, on all control-flow paths, then warn it should be declared `not_null`.
-* (Complex) If a function with pointer return value ensures it is not `nullptr` on all return paths, then warn the return type should be declared `not_null`.
+* (Simple) ((Foundation)) If a function checks a pointer parameter against `nullptr` before access, on all control-flow paths, then warn it should be declared `not_null`.
+* (Complex) If a function with pointer return value ensures it is not `nullptr` on all return paths, then warn the return type should be declared `not_null`.
@@ -1378,7 +1378,7 @@ What if there are fewer than `n` elements in the array pointed to by `q`? Then,
What if there are fewer than `n` elements in the array pointed to by `p`? Then, we read some probably unrelated memory.
Either is undefined behavior and a potentially very nasty bug.
-**Alternative**: Consider using explicit ranges,
+**Alternative**: Consider using explicit ranges,
void copy(array_view r, array_view r2); // copy r to r2
@@ -1406,7 +1406,7 @@ This `draw2()` passes the same amount of information to `draw()`, but makes the
**Exception**: Use `zstring` and `czstring` to represent a C-style, zero-terminated strings. But see ???.
-**Enforcement**:
+**Enforcement**:
* (Simple) ((Bounds)) Warn for any expression that would rely on implicit conversion of an array type to a pointer type. Allow exception for zstring/czstring pointer types.
* (Simple) ((Bounds)) Warn for any arithmetic operation on an expression of pointer type that results in a value of pointer type. Allow exception for zstring/czstring pointer types.
@@ -1438,7 +1438,7 @@ To really reduce the number of arguments, we need to bundle the arguments into h
template
OutputIterator merge(InputRange1 r1, InputRange2 r2, OutputIterator result);
-Grouping arguments into "bundles" is a general technique to reduce the number of arguments and to increase the opportunities for checking.
+Grouping arguments into "bundles" is a general technique to reduce the number of arguments and to increase the opportunities for checking.
**Note**: How many arguments are too many? Four arguments is a lot.
There are functions that are best expressed with four individual arguments, but not many.
@@ -1504,7 +1504,7 @@ This will force every derived class to compute a center -- even if that's non-tr
// ... no data members ...
};
-**Enforcement**: (Simple) Warn if a pointer to a class `C` is assigned to a pointer to a base of `C` and the base class contains data members.
+**Enforcement**: (Simple) Warn if a pointer to a class `C` is assigned to a pointer to a base of `C` and the base class contains data members.
@@ -1524,7 +1524,7 @@ This will force every derived class to compute a center -- even if that's non-tr
# F: Functions
-A function specifies an action or a computation that takes the system from one consistent state to the next. It is the fundamental building block of programs.
+A function specifies an action or a computation that takes the system from one consistent state to the next. It is the fundamental building block of programs.
It should be possible to name a function meaningfully, to specify the requirements of its argument, and clearly state the relationship between the arguments and the result. An implementation is not a specification. Try to think about what a function does as well as about how it does it.
Functions are the most critical part in most interfaces, so see the interface rules.
@@ -1587,7 +1587,7 @@ A function definition is a function declaration that also specifies the function
### F.1: "Package" meaningful operations as carefully named functions
**Reason**: Factoring out common code makes code more readable, more likely to be reused, and limit errors from complex code.
-If something is a well-specified action, separate it out from its surrounding code and give it a name.
+If something is a well-specified action, separate it out from its surrounding code and give it a name.
**Example, don't**:
@@ -1656,7 +1656,7 @@ This is a monolith that is tied to a specific input and will never find a anothe
is >> x;
// check for errors
return x;
- }
+ }
void print(ostream& os, int x)
{
@@ -1677,7 +1677,7 @@ If there was a need, we could further templatize `read()` and `print()` on the d
{
input >> value;
// check for errors
- }
+ }
auto print(auto& output, const auto& value)
{
@@ -1935,11 +1935,11 @@ If you have multiple values to return, [use a tuple](#Rf-T-multi) or similar mul
void find_all(const vector&, vector& out, int x); // place pointers to elements with value x in out
-**Exceptions**:
+**Exceptions**:
* For non-value types, such as types in an inheritance hierarchy, return the object by `unique_ptr` or `shared_ptr`.
* If a type is expensive to move (e.g., `array`), consider allocating it on the free store and return a handle (e.g., `unique_ptr`), or passing it in a non-`const` reference to a target object to fill (to be used as an out-parameter).
-* In the special case of allowing a caller to reuse an object that carries capacity (e.g., `std::string`, `std::vector`) across multiple calls to the function in an inner loop, treat it as an in/out parameter instead and pass by `&`. This one use of the more generally named "caller-allocated out" pattern.
+* In the special case of allowing a caller to reuse an object that carries capacity (e.g., `std::string`, `std::vector`) across multiple calls to the function in an inner loop, treat it as an in/out parameter instead and pass by `&`. This one use of the more generally named "caller-allocated out" pattern.
**For an "in-out" parameter:** Pass by non-`const` reference. This makes it clear to callers that the object is assumed to be modified.
@@ -2027,7 +2027,7 @@ When I call `length(r)` should I test for `r==nullptr` first? Should the impleme
**Enforcement**:
-* (Simple) ((Bounds)) Warn for any arithmetic operation on an expression of pointer type that results in a value of pointer type.
+* (Simple) ((Bounds)) Warn for any arithmetic operation on an expression of pointer type that results in a value of pointer type.
@@ -2076,7 +2076,7 @@ When I call `length(r)` should I test for `r==nullptr` first? Should the impleme
**See also**: [Support library](#S-support).
-**Enforcement**: (Complex) Warn where accesses to pointer parameters are bounded by other parameters that are integral types and suggest they could use `array_view` instead.
+**Enforcement**: (Complex) Warn where accesses to pointer parameters are bounded by other parameters that are integral types and suggest they could use `array_view` instead.
@@ -2312,7 +2312,7 @@ If you have performance justification to optimize for rvalues, overload on `&&`
**Reason**: A return value is self-documenting as an "output-only" value.
And yes, C++ does have multiple return values, by convention of using a `tuple`, with the extra convenience of `tie` at the call site.
-**Example**:
+**Example**:
int f( const string& input, /*output only*/ string& output_data ) { // BAD: output-only parameter documented in a comment
// ...
@@ -2380,7 +2380,7 @@ Importantly, that does not imply a transfer of ownership of the pointed-to objec
{
int x = 7;
// ...
- return &x; // Bad: returns pointer to object that is about to be destroyed
+ return &x; // Bad: returns pointer to object that is about to be destroyed
}
This applies to references as well:
@@ -2390,7 +2390,7 @@ This applies to references as well:
int x = 7;
// ...
return x; // Bad: returns refence to object that is about to be destroyed
- }
+ }
**See also**: [discussion of dangling pointer prevention](#???).
@@ -2543,12 +2543,12 @@ For passthrough functions that pass in parameters (by ordinary reference or by p
// at statement or expression scope -- a lambda is natural
vector v = lots_of_work();
for(int tasknum = 0; tasknum < max; ++tasknum) {
- pool.run([=, &v]{
- /*
+ pool.run([=, &v]{
+ /*
...
- ... process 1/max-th of v, the tasknum-th chunk
+ ... process 1/max-th of v, the tasknum-th chunk
...
- */
+ */
});
}
pool.join();
@@ -2596,7 +2596,7 @@ For passthrough functions that pass in parameters (by ordinary reference or by p
**Example**: This is a simple three-stage parallel pipeline. Each `stage` object encapsulates a worker thread and a queue, has a `process` function to enqueue work, and in its destructor automatically blocks waiting for the queue to empty before ending the thread.
void send_packets( buffers& bufs ) {
- stage encryptor ([] (buffer& b){ encrypt(b); });
+ stage encryptor ([] (buffer& b){ encrypt(b); });
stage compressor ([&](buffer& b){ compress(b); encryptor.process(b); });
stage decorator ([&](buffer& b){ decorate(b); compressor.process(b); });
for (auto& b : bufs) { decorator.process(b); }
@@ -2654,7 +2654,7 @@ Subsections:
**Reason**: Ease of comprehension. If data is related (for fundamental reasons), that fact should be reflected in code.
-**Example**:
+**Example**:
void draw(int x, int y, int x2, int y2); // BAD: unnecessary implicit relationships
void draw(Point from, Point to) // better
@@ -2671,7 +2671,7 @@ Subsections:
**Reason**: Ease of comprehension. The use of `class` alerts the programmer to the need for an invariant
-**Note**: An invariant is logical condition for the members of an object that a constructor must establish for the public member functions to assume. After the invariant is established (typically by a constructor) every member function can be called for the object. An invariant can be stated informally (e.g., in a comment) or more formally using `Expects`.
+**Note**: An invariant is logical condition for the members of an object that a constructor must establish for the public member functions to assume. After the invariant is established (typically by a constructor) every member function can be called for the object. An invariant can be stated informally (e.g., in a comment) or more formally using `Expects`.
**Example**:
@@ -2720,7 +2720,7 @@ For example, we can use a set of declarations of freestandanding functions in a
an abstract base class,
or a template fuction with concepts to represent an interface.
The most important issue is to explicitly distinguish between an interface and its implementation "details."
-Ideally, and typically, an interface is far more stable than its implementatio(s).
+Ideally, and typically, an interface is far more stable than its implementation(s).
**Enforcement**: ???
@@ -2830,7 +2830,7 @@ You need a reason (use cases) for using a hierarchy.
Point1 p11 { 1,2}; // make an object on the stack
Point1 p12 {p11}; // a copy
- auto p21 = make_unique(1,2); // make an object on the free store
+ auto p21 = make_unique(1,2); // make an object on the free store
auto p22 = p21.clone(); // make a copy
// ...
@@ -2936,7 +2936,7 @@ Copy and move rules:
* [C.64: A move operation should move and leave its source in a valid state](#Rc-move-semantic)
* [C.65: Make move assignment safe for self-assignment](#Rc-copy-self)
* [C.66: Make move operations `noexcept`](#Rc-move-noexcept)
-* [C.67: A base class should suppress copying, and provide a virtual `clone` instead if "copying" is desired](#Rc-copy-virtual)
+* [C.67: A base class should suppress copying, and provide a virtual `clone` instead if "copying" is desired](#Rc-copy-virtual)
Other default operations rules:
@@ -3028,7 +3028,7 @@ If you don't want a default operation, suppress it with `=delete`.
### C.22: Make default operations consistent
**Reason**: The default operations are conceptually a matched set. Their semantics is interrelated.
-Users will be surprised if copy/move construction and copy/move assignment do logically different things. Users will be surprised if constructors and destructors do not provide a consistent view of resource management. Users will be surprised if copy and move doesn't reflect the way constructors and destructors work.
+Users will be surprised if copy/move construction and copy/move assignment do logically different things. Users will be surprised if constructors and destructors do not provide a consistent view of resource management. Users will be surprised if copy and move doesn't reflect the way constructors and destructors work.
**Example; bad**:
@@ -3060,7 +3060,7 @@ These operations disagree about copy semantics. This will lead to confusion and
Does this class need a destructor is a surprisingly powerful design question.
For most classes the answer is "no" either because the class holds no resources or because destruction is handled by [the rule of zero](#Rc-zero);
that is, it's members can take care of themselves as concerns destruction.
-If the answer is "yes", much of the design of the class follows (see [the rule of five](#Rc-five).
+If the answer is "yes", much of the design of the class follows (see [the rule of five](#Rc-five).
@@ -3122,7 +3122,7 @@ The default destructor does it better, more efficiently, and can't get it wrong.
### C.31: All resources acquired by a class must be released by the class's destructor
-**Reason**: Prevention of resource leaks, especially in error cases.
+**Reason**: Prevention of resource leaks, especially in error cases.
**Note**: For resources represented as classes with a complete set of default operations, this happens automatically.
@@ -3250,7 +3250,7 @@ The default copy operation will just copy the `p1.p` into `p2.p` leading to a do
**Enforcement**:
* A class with a pointer data member is suspect.
-* A class with an `owner` should define its default operations.
+* A class with an `owner` should define its default operations.
@@ -3305,11 +3305,11 @@ Unless `Shape` has its [copy assignment `=deleted`](#Rc-copy-virtual), only the
**Enforcement**:
* A class with a reference data member is suspect.
-* A class with an `owner` reference should define its default operations.
+* A class with an `owner` reference should define its default operations.
-### C.35: A base class with a virtual function needs a virtual destructor
+### C.35: A base class with a virtual function needs a virtual destructor
**Reason**: To prevent undefined behavior.
If an application attempts to delete a derived class object through a base class pointer, the result is undefined if the base class's destructor is non-virtual.
@@ -3375,7 +3375,7 @@ The standard library requires that all classes it deals with have destructors th
**Note**: Many have tried to devise a fool-proof scheme for dealing with failure in destructors.
None have succeeded to come up with a general scheme.
-This can be be a real practical problem: For example, what about a sockets that won't close?
+This can be be a real practical problem: For example, what about a sockets that won't close?
The writer of a destructor does not know why the destructor is called and cannot "refuse to act" by throwing an exception.
See discussion.
To make the problem worse, many "close/release" operations are not retryable.
@@ -3458,7 +3458,7 @@ It is often a good idea to express the invariant as an `Ensure` on the construct
The `Rec2` constructor is redundant.
Also, the default for `int` would be better done as a [member initializer](#Rc-in-class initializer).
-**See also**: [construct valid object](#Rc-complete) and [constructor throws](#Rc-throw).
+**See also**: [construct valid object](#Rc-complete) and [constructor throws](#Rc-throw).
**Enforcement**:
@@ -3557,7 +3557,7 @@ The idiom of having constructors acquire resources and destructors release them
// ...
}
else {
- // ... handle error ...
+ // ... handle error ...
}
// ...
}
@@ -3649,7 +3649,7 @@ Setting a `Vector1` to empty after detecting an error is trivial.
**Reason**: Using in-class member initializers lets the compiler generate the function for you. The compiler-generated function can be more efficient.
-**Example; bad**:
+**Example; bad**:
class X1 { // BAD: doesn't use member initializers
string s;
@@ -3659,7 +3659,7 @@ Setting a `Vector1` to empty after detecting an error is trivial.
// ...
};
-**Example**:
+**Example**:
class X2 {
string s = "default";
@@ -3670,7 +3670,7 @@ Setting a `Vector1` to empty after detecting an error is trivial.
};
-**Enforcement**: (Simple) A default constructor should do more than just initialize member variables with constants.
+**Enforcement**: (Simple) A default constructor should do more than just initialize member variables with constants.
@@ -3709,7 +3709,7 @@ Setting a `Vector1` to empty after detecting an error is trivial.
### C.47: Define and initialize member variables in the order of member declaration
-**Reason**: To minimize confusion and errors. That is the order in which the initialization happens (independent of the order of member initializers).
+**Reason**: To minimize confusion and errors. That is the order in which the initialization happens (independent of the order of member initializers).
**Example; bad**:
@@ -3841,14 +3841,14 @@ How would a maintainer know whether `j` was deliberately uninitialized (probably
// ...
f(); // GOOD: virtual dispatch is safe
// ...
- }
+ }
public:
virtual void f() = 0;
template
static shared_ptr Create() // interface for creating objects
- {
+ {
auto p = make_shared();
p->PostInitialize();
return p;
@@ -4125,7 +4125,7 @@ After a copy `x` and `y` can be independent objects (value semantics, the way no
s = a.s;
i = a.i;
return *this;
- }
+ }
This is obviously safe and apparently efficient.
However, what if we do one self-assignment per million assignments?
@@ -4137,7 +4137,7 @@ Consider:
s = a.s;
i = a.i;
return *this;
- }
+ }
`std::string` is safe for self-assignment and so are `int`. All the cost is carried by the (rare) case of self-assignment.
@@ -4147,14 +4147,14 @@ Consider:
### C.63: Make move assignment non-`virtual`, take the parameter by `&&`, and return by non-`const `&`
-**Reason**: It is simple and efficient.
+**Reason**: It is simple and efficient.
**See**: [The rule for copy-assignment](#Rc-copy-assignment).
**Enforcement**: Equivalent to what is done for [copy-assignment](#Rc-copy-assignment).
* (Simple) An assignment operator should not be virtual. Here be dragons!
* (Simple) An assignment operator should return `T&` to enable chaining, not alternatives like `const T&` which interfere with composability and putting objects in containers.
-* (Moderate) A move assignment operator should (implicitly or explicitly) invoke all base and member move assignment operators.
+* (Moderate) A move assignment operator should (implicitly or explicitly) invoke all base and member move assignment operators.
@@ -4190,9 +4190,9 @@ Consider:
// ...
X y = std::move(x);
x = X{}; // OK
- } // OK: x can be destroyed
+ } // OK: x can be destroyed
-**Note**: Ideally, that moved-from should be the default value of the type. Ensure that unless there is an exceptionally good reason not to. However, not all types have a default value and for some types establishing the default value can be expensive. The standard requires only that the moved-from object can be destroyed.
+**Note**: Ideally, that moved-from should be the default value of the type. Ensure that unless there is an exceptionally good reason not to. However, not all types have a default value and for some types establishing the default value can be expensive. The standard requires only that the moved-from object can be destroyed.
Often, we can easily and cheaply do better: The standard library assumes that it it possible to assign to a moved-from object. Always leave the moved-from object in some (necessarily specified) valid state.
**Note**: Unless there is an exceptionally strong reason not to, make `x=std::move(y); y=z;` work with the conventional semantics.
@@ -4221,11 +4221,11 @@ Often, we can easily and cheaply do better: The standard library assumes that it
s = std::move(a.s);
i = a.i;
return *this;
- }
+ }
The one-in-a-million argument against `if (this==&a) return *this;` tests from the discussion of [self-assignment](#Rc-copy self) is even more relevant for self-move.
-**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).
+**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).
**Note** The ISO standard guarantees only a "valid but unspecified" state for the standard library containers. Apparently this has not been a problem in about 10 years of experimental and production use. Please contact the editors if you find a counter example. The rule here is more caution and insists on complete safety.
@@ -4240,7 +4240,7 @@ The one-in-a-million argument against `if (this==&a) return *this;` tests from t
**Enforcement**:
* (Moderate) In the case of self-assignment, a move assignment operator should not leave the object holding pointer members that have been `delete`d or set to nullptr.
-* (Not enforceable) Look at the use of standard-library container types (incl. `string`) and consider them safe for ordinary (not life-critical) uses.
+* (Not enforceable) Look at the use of standard-library container types (incl. `string`) and consider them safe for ordinary (not life-critical) uses.
@@ -4283,20 +4283,20 @@ This `Vector2` is not just inefficient, but since a vector copy requires allocat
-### C.67: A base class should suppress copying, and provide a virtual `clone` instead if "copying" is desired
+### C.67: A base class should suppress copying, and provide a virtual `clone` instead if "copying" is desired
**Reason**: To prevent slicing, because the normal copy operations will copy only the base portion of a derived object.
**Example; bad**:
class B { // BAD: base class doesn't suppress copying
- int data;
+ int data;
// ... nothing about copy operations, so uses default ...
};
class D : public B {
string moredata; // add a data member
- // ...
+ // ...
};
auto d = make_unique();
@@ -4305,16 +4305,16 @@ This `Vector2` is not just inefficient, but since a vector copy requires allocat
**Example**:
class B { // GOOD: base class suppresses copying
- B(const B&) =delete;
+ B(const B&) =delete;
B& operator=(const B&) =delete;
- virtual unique_ptr clone() { return /* B object */; }
+ virtual unique_ptr clone() { return /* B object */; }
// ...
};
class D : public B {
string moredata; // add a data member
- unique_ptr clone() override { return /* D object */; }
- // ...
+ unique_ptr clone() override { return /* D object */; }
+ // ...
};
auto d = make_unique();
@@ -4373,12 +4373,12 @@ Writing out the bodies of the copy and move operations is verbose, tedious, and
### C.81: Use `=delete` when you want to disable default behavior (without wanting an alternative)
-**Reason**: In a few cases, a default operation is not desirable.
+**Reason**: In a few cases, a default operation is not desirable.
**Example**:
class Immortal {
- public:
+ public:
~Immortal() = delete; // do not allow destruction
// ...
};
@@ -4399,7 +4399,7 @@ Writing out the bodies of the copy and move operations is verbose, tedious, and
explicit unique_ptr(pointer p) noexcept;
// ...
unique_ptr(unique_ptr&& u) noexcept; // move constructor
- // ...
+ // ...
unique_ptr(const unique_ptr&) = delete; // disable copy from lvalue
// ...
};
@@ -4633,7 +4633,7 @@ Summary:
* [F.50: Use a lambda when a function won't do (to capture local variables, or to write a local function)](#Rf-capture-vs-overload)
* [F.52: Prefer capturing by reference in lambdas that will be used locally, including passed to algorithms](#Rf-reference-capture)
* [F.53: Avoid capturing by reference in lambdas that will be used nonlocally, including returned, stored on the heap, or passed to another thread](#Rf-value-capture)
-* [ES.28: Use lambdas for complex initialization, especially of `const` variables](#Res-lambda-init)
+* [ES.28: Use lambdas for complex initialization, especially of `const` variables](#Res-lambda-init)
@@ -4690,7 +4690,7 @@ Do *not* use inheritance when simply having a data member will do. Usually this
??? Good old Shape example?
-**Example, bad**:
+**Example, bad**:
Do *not* represent non-hierarchical domain concepts as class hierarchies.
template
@@ -4728,7 +4728,7 @@ not using this (over)general interface in favor of a particular interface found
**Reason**: A class is more stable (less brittle) if it does not contain data. Interfaces should normally be composed entirely of public pure virtual functions.
-**Example**:
+**Example**:
???
@@ -4767,7 +4767,7 @@ not using this (over)general interface in favor of a particular interface found
* In extremely rare cases, you might find a reasonable for an abstract class to have a bit of data shared by all derived classes
(e.g., use statistics data, debug information, etc.); such classes tend to have constructors. But be warned: Such classes also tend to be prone to requiring virtual inheritance.
-**Enforcement**: Flag abstract classes with constructors.
+**Enforcement**: Flag abstract classes with constructors.
@@ -4959,7 +4959,7 @@ This kind of "vector" isn't meant to be used as a base class at all.
???
**Note**: This is a very common use of inheritance because the need for multiple different interfaces to an implementation is common
-and such interfaces are often not easily or naturally organized into a single-rooted hierarchy.
+and such interfaces are often not easily or naturally organized into a single-rooted hierarchy.
**Note**: Such interfaces are typically abstract classes.
@@ -5061,7 +5061,7 @@ Both `d`s are sliced.
void user(B* pb)
{
- if (D* pd = dynamic_cast(pb)) {
+ if (D* pd = dynamic_cast(pb)) {
// ... use D's interface ...
}
else {
@@ -5175,7 +5175,7 @@ It also gives an opportunity to eliminate a separate allocation for the referenc
void use(B*);
- D a[] = { {1,2}, {3,4}, {5,6} };
+ D a[] = { {1,2}, {3,4}, {5,6} };
B* p = a; // bad: a decays to &a[0] which is converted to a B*
p[1].x = 7; // overwrite D[0].y
@@ -5319,7 +5319,7 @@ The string allocated for `s` and assigned to `p` is destroyed before it can be u
void f(double);
auto f = [](char); // error: cannot overload variable and function
- auto g = [](int) { /* ... */ };
+ auto g = [](int) { /* ... */ };
auto g = [](double) { /* ... */ }; // error: cannot overload variables
auto h = [](auto) { /* ... */ }; // OK
@@ -5487,7 +5487,7 @@ There are a few cases where leaks can be acceptable or even optimal:
if you are writing a program that simply produces an output based on an input and the amount of memory needed is proportional to the size of the input,
the optimal strategy (for performance and ease of programming) is sometimes simply never to delete anything.
If you have enough memory to handle your largest input, leak away, but be sure to give a good error message if you are wrong.
-Here, we ignore such cases.
+Here, we ignore such cases.
Resource management rule summary:
@@ -5540,7 +5540,7 @@ Alocation and deallocation rule summary:
// ...
Send(port, x);
// ...
- my_mutex.unlock();
+ my_mutex.unlock();
ClosePort(port);
delete x;
}
@@ -5558,7 +5558,7 @@ Further, if any of the code marked `...` throws an exception, then `x` is leaked
// ...
} // automatically unlocks my_mutex and deletes the pointer in x
-Now all resource cleanup is automatic, performed once on all paths whether or not there is an exception. As a bonus, the function now advertises that it takes over ownership of the pointer.
+Now all resource cleanup is automatic, performed once on all paths whether or not there is an exception. As a bonus, the function now advertises that it takes over ownership of the pointer.
What is `Port`? A handy wrapper that encapsulates the resource:
@@ -5569,7 +5569,7 @@ What is `Port`? A handy wrapper that encapsulates the resource:
~Port() { ClosePort(port); }
operator PortHandle() { return port; }
- // port handles can't usually be cloned, so disable copying and assignment if necessary
+ // port handles can't usually be cloned, so disable copying and assignment if necessary
Port(const Port&) =delete;
Port& operator=(const Port&) =delete;
};
@@ -5665,7 +5665,7 @@ It is simply a (most valuable) indicator to programmers and analysis tools.
For example, if an `owner` is a member of a class, that class better have a destructor that `delete`s it.
**Example**, bad:
-Returning a (raw) pointer imposes a life-time management burden on the caller; that is, who deletes the pointed-to object?
+Returning a (raw) pointer imposes a life-time management burden on the caller; that is, who deletes the pointed-to object?
Gadget* make_gadget(int n)
{
@@ -5677,7 +5677,7 @@ Returning a (raw) pointer imposes a life-time management burden on the caller; t
void caller(int n)
{
auto p = make_gadget(n); // remember to delete p
- // ...
+ // ...
delete p;
}
@@ -5703,7 +5703,7 @@ return a "smart pointer."
* (Moderate) Warn on failure to either `reset` or explicitly `delete` an `owner` pointer on every code path.
* (Simple) Warn if the return value of `new` or a function call with return value of pointer type is assigned to a raw pointer.
* (Simple) Warn if a function returns an object that was allocated within the function but has a move constructor.
-Suggest considering returning it by value instead.
+Suggest considering returning it by value instead.
@@ -5804,7 +5804,7 @@ They are a notable source of errors.
// ...
delete p1; // error: cannot delete object allocated by malloc()
- free(p2); // error: cannot free() object allocatedby new
+ free(p2); // error: cannot free() object allocatedby new
}
In some implementaions that `delete` and that `free()` might work, or maybe they will cause run-time errors.
@@ -5966,7 +5966,7 @@ This will leak the object used to initialize `p1` (only).
**Example, bad**: This needlessly adds and maintains a reference count
- void f()
+ void f()
{
shared_ptr base = make_shared();
// use base locally, without copying it -- refcount never exceeds 1
@@ -5974,14 +5974,14 @@ This will leak the object used to initialize `p1` (only).
**Example**: This is more efficient
- void f()
+ void f()
{
unique_ptr base = make_unique();
// use base locall
} // destroy base
-**Enforcement**: (Simple) Warn if a function uses a `Shared_ptr` with an object allocated within the function, but never returns the `Shared_ptr` or passes it to a function requiring a `Shared_ptr&`. Suggest using `unique_ptr` instead.
+**Enforcement**: (Simple) Warn if a function uses a `Shared_ptr` with an object allocated within the function, but never returns the `Shared_ptr` or passes it to a function requiring a `Shared_ptr&`. Suggest using `unique_ptr` instead.
@@ -6039,17 +6039,17 @@ Any type (including primary template or specialization) that overloads unary `*`
**Example**:
- // use Boost's intrusive_ptr
+ // use Boost's intrusive_ptr
#include
void f(boost::intrusive_ptr p) { // error under rule 'sharedptrparam'
p->foo();
- }
+ }
- // use Microsoft's CComPtr
+ // use Microsoft's CComPtr
#include
void f(CComPtr p) { // error under rule 'sharedptrparam'
p->foo();
- }
+ }
Both cases are an error under the [`sharedptrparam` guideline](#Rr-smartptrparam):
`p` is a `Shared_ptr`, but nothing about its sharedness is used here and passing it by value is a silent pessimization;
@@ -6123,7 +6123,7 @@ A function that does not manipulate lifetime should take raw pointers or referen
* (Simple) Warn if a function takes a `Unique_ptr` parameter by lvalue reference and does not either assign to it or call `reset()` on it on at least one code path. Suggest taking a `T*` or `T&` instead.
* (Simple) ((Foundation)) Warn if a function takes a `Unique_ptr` parameter by reference to `const`. Suggest taking a `const T*` or `const T&` instead.
-* (Simple) ((Foundation)) Warn if a function takes a `Unique_ptr` parameter by rvalue reference. Suggest using pass by value instead.
+* (Simple) ((Foundation)) Warn if a function takes a `Unique_ptr` parameter by rvalue reference. Suggest using pass by value instead.
@@ -6146,7 +6146,7 @@ A function that does not manipulate lifetime should take raw pointers or referen
* (Simple) Warn if a function takes a `Unique_ptr` parameter by lvalue reference and does not either assign to it or call `reset()` on it on at least one code path. Suggest taking a `T*` or `T&` instead.
* (Simple) ((Foundation)) Warn if a function takes a `Unique_ptr` parameter by reference to `const`. Suggest taking a `const T*` or `const T&` instead.
-* (Simple) ((Foundation)) Warn if a function takes a `Unique_ptr` parameter by rvalue reference. Suggest using pass by value instead.
+* (Simple) ((Foundation)) Warn if a function takes a `Unique_ptr` parameter by rvalue reference. Suggest using pass by value instead.
@@ -6166,7 +6166,7 @@ A function that does not manipulate lifetime should take raw pointers or referen
* (Simple) Warn if a function takes a `Shared_ptr` parameter by lvalue reference and does not either assign to it or call `reset()` on it on at least one code path. Suggest taking a `T*` or `T&` instead.
* (Simple) ((Foundation)) Warn if a function takes a `Shared_ptr` by value or by reference to `const` and does not copy or move it to another `Shared_ptr` on at least one code path. Suggest taking a `T*` or `T&` instead.
-* (Simple) ((Foundation)) Warn if a function takes a `Shared_ptr` by rvalue reference. Suggesting taking it by value instead.
+* (Simple) ((Foundation)) Warn if a function takes a `Shared_ptr` by rvalue reference. Suggesting taking it by value instead.
@@ -6188,7 +6188,7 @@ A function that does not manipulate lifetime should take raw pointers or referen
* (Simple) Warn if a function takes a `Shared_ptr` parameter by lvalue reference and does not either assign to it or call `reset()` on it on at least one code path. Suggest taking a `T*` or `T&` instead.
* (Simple) ((Foundation)) Warn if a function takes a `Shared_ptr` by value or by reference to `const` and does not copy or move it to another `Shared_ptr` on at least one code path. Suggest taking a `T*` or `T&` instead.
-* (Simple) ((Foundation)) Warn if a function takes a `Shared_ptr` by rvalue reference. Suggesting taking it by value instead.
+* (Simple) ((Foundation)) Warn if a function takes a `Shared_ptr` by rvalue reference. Suggesting taking it by value instead.
@@ -6208,7 +6208,7 @@ A function that does not manipulate lifetime should take raw pointers or referen
* (Simple) Warn if a function takes a `Shared_ptr` parameter by lvalue reference and does not either assign to it or call `reset()` on it on at least one code path. Suggest taking a `T*` or `T&` instead.
* (Simple) ((Foundation)) Warn if a function takes a `Shared_ptr` by value or by reference to `const` and does not copy or move it to another `Shared_ptr` on at least one code path. Suggest taking a `T*` or `T&` instead.
-* (Simple) ((Foundation)) Warn if a function takes a `Shared_ptr` by rvalue reference. Suggesting taking it by value instead.
+* (Simple) ((Foundation)) Warn if a function takes a `Shared_ptr` by rvalue reference. Suggesting taking it by value instead.
@@ -6219,7 +6219,7 @@ Functions should prefer to pass raw pointers and references down call chains.
At the top of the call tree where you obtain the raw pointer or reference from a smart pointer that keeps the object alive.
You need to be sure that smart pointer cannot be inadvertently be reset or reassigned from within the call tree below
-**Note**: To do this, sometimes you need to take a local copy of a smart pointer, which firmly keeps the object alive for the duration of the function and the call tree.
+**Note**: To do this, sometimes you need to take a local copy of a smart pointer, which firmly keeps the object alive for the duration of the function and the call tree.
**Example**: Consider this code:
@@ -6235,7 +6235,7 @@ You need to be sure that smart pointer cannot be inadvertently be reset or reass
g_p = ... ; // oops, if this was the last shared_ptr to that widget, destroys the widget
}
-The following should not pass code review:
+The following should not pass code review:
void my_code() {
f( *g_p ); // BAD: passing pointer or reference obtained from a nonlocal smart pointer
@@ -6288,7 +6288,7 @@ Declaration rules:
* [ES.25: Declare an object `const` or `constexpr` unless you want to modify its value later on](#Res-const)
* [ES.26: Don't use a variable for two unrelated purposes](#Res-recycle)
* [ES.27: Use `std::array` or `stack_array` for arrays on the stack](#Res-stack)
-* [ES.28: Use lambdas for complex initialization, especially of `const` variables](#Res-lambda-init)
+* [ES.28: Use lambdas for complex initialization, especially of `const` variables](#Res-lambda-init)
* [ES.30: Don't use macros for program text manipulation](#Res-macros)
* [ES.31: Don't use macros for constants or "functions"](#Res-macros2)
* [ES.32: Use `ALL_CAPS` for all macro names](#Res-CAPS!)
@@ -6344,7 +6344,7 @@ Arithmetic rules:
**Reason**: Code using a library can be much easier to write than code working directly with language features, much shorter, tend to be of a higher level of abstraction, and the library code is presumably already tested.
The ISO C++ standard library is among the most widely know and best tested libraries.
-It is available as part of all C++ Implementations.
+It is available as part of all C++ Implementations.
**Example**:
@@ -6427,7 +6427,7 @@ A declaration is a statement. a declaration introduces a name into a scope and m
// ...deal with Circle ...
}
else {
- // ... handle error ...
+ // ... handle error ...
}
}
@@ -6482,18 +6482,18 @@ I am assuming that `Record` is large and doesn't have a good move operation so t
v.push_back(s);
for (int i=0; i<20; ++i) { // good: i is local to for-loop
- //* ...
- }
+ //* ...
+ }
if (auto pc = dynamic_cast(ps)) { // good: pc is local to if-statement
// ...deal with Circle ...
}
else {
- // ... handle error ...
+ // ... handle error ...
}
}
-**Enforcement**:
+**Enforcement**:
* Flag loop variables declared before the loop and not used after the loop
* (hard) Flag loop variables declared before the loop andused after the loop for an unrelated purpose.
@@ -6507,7 +6507,7 @@ I am assuming that `Record` is large and doesn't have a good move operation so t
**Example**: Conventional short, local names increase readability:
template // good
- void print(ostream& os, const vector& v)
+ void print(ostream& os, const vector& v)
{
for (int i = 0; i>i;
+ cin>>i;
In the not uncommon case where the input target and the input operation get separated (as they should not) the possibility of used-before-set opens up.
@@ -6791,7 +6791,7 @@ Don't flag variables of user-defined types with default constructors.
// ... no use of s here ...
s = "what a waste";
-**Example, bad**:
+**Example, bad**:
SomeLargeType var; // ugly CaMeLcAsEvArIaBlE
@@ -6911,7 +6911,7 @@ If `leak==true` the object pointer to by `p2` is leaked and the object pointed t
### ES.25: Declare an objects `const` or `constexpr` unless you want to modify its value later on
-**Reason**: That way you can't change the value by mistake. That way may offer the compiler optimization opportunities.
+**Reason**: That way you can't change the value by mistake. That way may offer the compiler optimization opportunities.
**Example**:
@@ -6974,7 +6974,7 @@ The definition of `a2` is C but not C++ and is considered a security risk
void f()
{
array a1;
- stack_array a2(m);
+ stack_array a2(m);
// ...
}
@@ -7019,7 +7019,7 @@ The definition of `a2` is C but not C++ and is considered a security risk
If at all possible, reduce the conditions to a simple set of alternatives (e.g., an `enum`) and don't mix up selection and initialization.
-**Example**:
+**Example**:
owner in = [&]{
switch (source) {
@@ -7028,7 +7028,7 @@ If at all possible, reduce the conditions to a simple set of alternatives (e.g.,
case file: owned=true; return *new ifstream{argv[2]};
}();
-**Enforcement:** Hard. At best a heuristic. Look for an unitialized variable followed by a loop assigning to it.
+**Enforcement:** Hard. At best a heuristic. Look for an unitialized variable followed by a loop assigning to it.
@@ -7103,7 +7103,7 @@ Even if we hadn't left a well-know bug in `SQUARE` there are much better behaved
-## ES.stmt: Statements
+## ES.stmt: Statements
Statements control the flow of control (except for function calls and exception throws, which are expressions).
@@ -7156,18 +7156,18 @@ rather than
for(auto& x : v) // OK
cout << x << '\n';
- for(int i=1; i
### ES.42: Keep use of pointers simple and straightforward
-**Reason**: Complicated pointer manipulation is a major source of errors.
+**Reason**: Complicated pointer manipulation is a major source of errors.
* Do all pointer arithmetic on an `array_view` (exception ++p in simple loop???)
* Avoid pointers to pointers
@@ -7543,9 +7543,9 @@ We also include lossy arithmetic casts, such as from a negative floating point t
-### ES.47: Use `nullptr` rather than `0` or `NULL`
+### ES.47: Use `nullptr` rather than `0` or `NULL`
-**Reason**: Readibility. Minimize surprises: `nullptr` cannot be confused with an `int`.
+**Reason**: Readibility. Minimize surprises: `nullptr` cannot be confused with an `int`.
**Example**: Consider
@@ -7560,7 +7560,7 @@ We also include lossy arithmetic casts, such as from a negative floating point t
### ES.48: Avoid casts
-**Reason**: Casts are a well-known source of errors. Makes some optimizations unreliable.
+**Reason**: Casts are a well-known source of errors. Makes some optimizations unreliable.
**Example**:
@@ -7872,7 +7872,7 @@ Performance rule summary:
### PER.3: Don't optimize something that's not performance critical
-**Reason**: Optimizing a non-performance-critical part of a program has no effect on system performance.
+**Reason**: Optimizing a non-performance-critical part of a program has no effect on system performance.
**Note**: If your program spends most of its time waiting for the web or for a human, optimization of in-memory computation is problably useless.
@@ -8014,7 +8014,7 @@ See also:
**Reason**: It is hard to be certain that concurrency isn't used now or sometime in the future.
Code gets re-used.
-Libraries using threads my be used from some other part of the program.
+Libraries using threads my be used from some other part of the program.
**Example**:
@@ -8111,7 +8111,7 @@ Error handling involves
* Transmitting information about an error to some handler code
* Preserve the state of a program in a valid state
* Avoid resource leaks
-*
+*
It is not possible to recover from all errors. If recovery from an error is not possible, it is important to quickly "get out" in a well-defined way. A strategy for error handling must be simple, or it becomes a source of even worse errors.
The rules are designed to help avoid several kinds of errors:
@@ -8398,7 +8398,7 @@ Here, I know that `compute` will not throw because it is composed out of operati
The `noexcept` here states that I am not willing or able to handle the situation where I cannot construct the local `vector`. That is, I consider memory exhaustion a serious design error (on line with hardware failures) so that I'm willing to crash the program if it happens.
-**See also**: [discussion](#Sd-noexcept).
+**See also**: [discussion](#Sd-noexcept).
@@ -8421,7 +8421,7 @@ One way of avoiding such problems is to use resource handles consistently:
void no_leak(int x)
{
auto p = make_unique(7);
- int (x<0) throw Get_me_out_of_here{}; // will delete *p if necessary
+ int (x<0) throw Get_me_out_of_here{}; // will delete *p if necessary
// ...
// no need for delete p
}
@@ -8483,7 +8483,7 @@ One way of avoiding such problems is to use resource handles consistently:
void my_code() // Don't
{
// ...
- throw runtime_error{"moon in the 4th quarter"};
+ throw runtime_error{"moon in the 4th quarter"};
// ...
}
@@ -8520,7 +8520,7 @@ One way of avoiding such problems is to use resource handles consistently:
// ...
}
-Instead, use
+Instead, use
catch (exception& e) { /* ... */ }
@@ -8797,7 +8797,7 @@ Other template rules summary:
-## T.gp: Generic programming
+## T.gp: Generic programming
Generic programming is programming using types and algorithms parameterized by types, values, and algorithms.
@@ -8871,7 +8871,7 @@ is to efficiently generalize operations/algorithms over a set of types with simi
Iter find(Iter b, Iter e, Val v)
{
// ...
- }
+ }
**Note**: Don't use a template unless you have a realistic need for more than one template argument type.
Don't overabstract.
@@ -8947,7 +8947,7 @@ See [Stable base](#Rt-abi).
};
// implementations
- template*...*/>
+ template*...*/>
class ConcreteCommand : public Command {
// implement virtuals
};
@@ -9000,7 +9000,7 @@ The assumed meaning (syntax and semantics) of a template argument is fundamental
A concept dramatically improves documentation and error handling for the template.
Specifying concepts for template arguments is a powerful design tool.
-**Example**:
+**Example**:
template
requires Input_iterator
@@ -9120,7 +9120,7 @@ The shorter versions better match the way we speak. Note that many templates don
**Reason**: Concepts are meant to express semantic notions, such as "a number", "a range" of elements, and "totally ordered."
Simple constraints, such as "has a `+` operator" and "has a `>` operator" cannot be meaningfully specified in isolation
-and should be used only as building blocks for meaningful concepts, rather than in user code.
+and should be used only as building blocks for meaningful concepts, rather than in user code.
**Example, bad**:
@@ -9171,7 +9171,7 @@ This `Addable` violates the mathematical rule that addition is supposed to be co
**Note**: Concepts with multiple operations have far lower chance of accidentally matching a type than a single-operation concept.
-**Enforcement**:
+**Enforcement**:
* Flag single-operation `concepts` when used outside the definition of other `concepts`.
* Flag uses of `enable_if` that appears to simulate single-operation `concepts`.
@@ -9543,7 +9543,7 @@ Note the use of the `s` suffix to ensire that the string is a `std::string`, rat
### T.60: Minimize a template's context dependencies
-**Reason**: Eases understanding. Minimizes errors from unexpected dependencies. Eases tool creation.
+**Reason**: Eases understanding. Minimizes errors from unexpected dependencies. Eases tool creation.
**Example**:
@@ -9839,7 +9839,7 @@ Note that the assignment in `maul2()` violated the no-slicing [Rule](#???).
**Example**:
- ???
+ ???
**Enforcement**: ???
@@ -9973,7 +9973,7 @@ Templates provide a general mechanism for compile-time programming.
Metaprogramming is programming where at least one input or one result is a type.
Templates offer Turing-complete (modulo memory capacity) duck typing at compile time.
-The syntax and techniques needed are pretty horrendous.
+The syntax and techniques needed are pretty horrendous.
@@ -10033,7 +10033,7 @@ Use cases that require concepts (e.g. overloading based on concepts) are among t
**Reason**: Template metaprogramming is the only directly supported and half-way principled way of generating types at compile time.
-**Note**: "Traits" techniques are mostly replaced by template aliases to compute types and `constexpr` functions to compute values.
+**Note**: "Traits" techniques are mostly replaced by template aliases to compute types and `constexpr` functions to compute values.
**Example**:
@@ -10550,7 +10550,7 @@ Complicates conversion to use language-supported modules (when they become avail
**Reason**: It is almost always a bug to mention an unnamed namespace in a header file.
-**Example**:
+**Example**:
???
@@ -10588,7 +10588,7 @@ Standard-library rule summary:
-### STL.1: Use libraries wherever possible
+### STL.1: Use libraries wherever possible
**Reason**: Save time. Don't re-invent the wheel.
Don't replicate the work of others.
@@ -10717,7 +10717,7 @@ C++03 and (reasonably) a bit backwards looking.
Too timid and reflects its 1990s origins.
[A critique from 2014](https://www.linkedin.com/pulse/20140503193653-3046051-why-google-style-guide-for-c-is-a-deal-breaker).
Google are busy updating their code base and we don't know how accurately the posted guideline reflects their actual code.
-This set of recommendations is evolving.
+This set of recommendations is evolving.
* [JSF++: JOINT STRIKE FIGHTER AIR VEHICLE C++ CODING STANDARDS](http://www.stroustrup.com/JSF-AV-rules.pdf).
Document Number 2RDU00001 Rev C. December 2005.
For flight control software.
@@ -10751,7 +10751,7 @@ Somewhat brief, pre-C++11, and (not unreasonably) adjusted to its domain.
* Sutter and Alexandrescu: C++ Coding Standards. Addison-Wesley 2005. More a set of meta-rules than a set of rules. Pre-C++11. Recommended.
*
Bjarne Stroustrup: [A rationale for semantically enhanced library languages](http://www.stroustrup.com/SELLrationale.pdf).
-LCSD05. October 2005.
+LCSD05. October 2005.
* Stroustrup: [A Tour of C++](http://www.stroustrup.com/Tour.html).
Addison Wesley 2014.
Each chapter ends with an advice section consisting of a set of recommendations.
@@ -10789,7 +10789,7 @@ Primarily a teaching tool.
* Bjarne Stroustrup: [C++11 Style](http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Keynote-Bjarne-Stroustrup-Cpp11-Style). 2012.
* Bjarne Stroustrup: [The Essence of C++: With Examples in C++84, C++98, C++11, and C++14](http://channel9.msdn.com/Events/GoingNative/2013/Opening-Keynote-Bjarne-Stroustrup). 2013
-* All the talks from [CppCon '14](https://isocpp.org/blog/2014/11/cppcon-videos-c9)
+* All the talks from [CppCon '14](https://isocpp.org/blog/2014/11/cppcon-videos-c9)
* Bjarne Stroustrup: [The essence of C++](https://www.youtube.com/watch?v=86xWVb4XIyE) at the University of Edinburgh. 2014.
* Sutter: ???
* ??? more ???
@@ -10878,8 +10878,8 @@ Use of these casts can violate type safety and cause the program to access a var
class derived1 : public base { };
- class derived2 : public base {
- std::string s;
+ class derived2 : public base {
+ std::string s;
public:
std::string get_s() { return s; }
};
@@ -10912,7 +10912,7 @@ Casting away `const` is a lie. If the variable is actually declared `const`, it'
f(j); // undefined behavior
-**Exception**: You may need to cast away `const` when calling `const`-incorrect functions. Prefer to wrap such functions in inline `const`-correct wrappers to encapsulate the cast in one place.
+**Exception**: You may need to cast away `const` when calling `const`-incorrect functions. Prefer to wrap such functions in inline `const`-correct wrappers to encapsulate the cast in one place.
**Enforcement**: Issue a diagnostic for any use of `const_cast`. To fix: Either don't use the variable in a non-`const` way, or don't make it `const`.
@@ -10933,8 +10933,8 @@ Note that a C-style `(T)expression` cast means to perform the first of the follo
class derived1 : public base { };
- class derived2 : public base {
- std::string s;
+ class derived2 : public base {
+ std::string s;
public:
std::string get_s() { return s; }
};
@@ -10984,7 +10984,7 @@ Before a variable has been initialized, it does not contain a deterministic vali
**Enforcement**:
- Issue a diagnostic for any constructor of a non-trivially-constructible type that does not initialize all member variables. To fix: Write a data member initializer, or mention it in the member initializer list.
- - Issue a diagnostic when constructing an object of a trivially constructible type without `()` or `{}` to initialize its members. To fix: Add `()` or `{}`.
+ - Issue a diagnostic when constructing an object of a trivially constructible type without `()` or `{}` to initialize its members. To fix: Add `()` or `{}`.
@@ -11056,7 +11056,7 @@ For the purposes of this document, bounds-safety is defined to be the property t
The following are under consideration but not yet in the rules below, and may be better in other profiles:
- -
+ -
An implementation of this profile shall recognize the following patterns in source code as non-conforming and issue a diagnostic.
@@ -11132,7 +11132,7 @@ Dynamic accesses into arrays are difficult for both tools and humans to validate
**Example; good**:
- // ALTERNATIVE A: Use an array_view
+ // ALTERNATIVE A: Use an array_view
// A1: Change parameter type to use array_view
void f(array_view a, int pos)
@@ -11167,7 +11167,7 @@ Dynamic accesses into arrays are difficult for both tools and humans to validate
**Example; good**:
- // ALTERNATIVE A: Use an array_view
+ // ALTERNATIVE A: Use an array_view
void f()
{
int arr[COUNT];
@@ -11318,12 +11318,12 @@ References are never owners.
The names are mostly ISO standard-library style (lower case and underscore):
* `T*` // The `T*` is not an owner, may be `nullptr` (Assumed to be pointing to a single element)
-* `char*` // A C-style string (a zero-terminated array of characters); can be `nullptr`
-* `const char*` // A C-style string; can be `nullptr`
+* `char*` // A C-style string (a zero-terminated array of characters); can be `nullptr`
+* `const char*` // A C-style string; can be `nullptr`
* `T&` // The `T&` is not an owner, may not be `&(T&)*nullptr` (language rule)
The "raw-pointer" notation (e.g. `int*`) is assumed to have its most common meaning; that is, a pointer points to an object, but does not own it.
-Owners should be converted to resource handles (e.g., `unique_ptr` or `vector`) or marked `owner`
+Owners should be converted to resource handles (e.g., `unique_ptr` or `vector`) or marked `owner`
* `owner` // a `T*`that owns the object pointed/referred to; can be `nullptr`
* `owner` // a `T&` that owns the object pointed/referred to
@@ -11363,7 +11363,7 @@ and they make the distinction between a pointer to one `char` and a pointer to a
A sequence of characters that is not assumed to be zero-terminated sould be a `char*`, rather than a `zstring`.
French accent optional.
-Use `not_null` for C-style strings that cannot be `nullptr`. ??? Do we need a name for `not_null`? or is its ugliness a feature?
+Use `not_null` for C-style strings that cannot be `nullptr`. ??? Do we need a name for `not_null`? or is its ugliness a feature?
@@ -11777,7 +11777,7 @@ Often, the old code is both a mess (hard to understand) and working correctly (f
Typically, the original programmer is not around and test cases incomplete.
The fact that the code is a mess dramatically increases to effort needed to make any change and the risk of introducing errors.
Often messy, old code runs unnecessarily slowly because it requires outdated compilers and cannot take advantage of modern hardware.
-In many cases, programs support would be required for major upgrade efforts.
+In many cases, programs support would be required for major upgrade efforts.
The purpose of modernizing code is to simplify adding new functionality, to ease maintenance, and to increase performance (throughput or latency), and to better utilize modern hardware.
Making code "look pretty" or "follow modern style" are not by themselves reasons for change.
@@ -11797,7 +11797,7 @@ In most circumstances, it is also impossible.
* We could start by focusing on the interfaces, e.g., make sure that no resources are lost and no pointer is misused.
This would be a set of changes across the whole code base, but would most likely have huge benefits.
-Wichever way you choose, please note that the most advantages come with the highest conformance to the guidelines.
+Whichever way you choose, please note that the most advantages come with the highest conformance to the guidelines.
The guidelines are not a random set of unrelated rules where you can srandomly pick and choose with an expectation of success.
We would dearly love to hear about experience and about tools used.
@@ -11812,7 +11812,7 @@ In particular, here we present further rationale, longer examples, and discussio
### Discussion: Define and initialize member variables in the order of member declaration
-Member variables are always initialized in the order they are declared in the class definition, so write them in that order in the constructor initialization list. Writing them in a different order just makes the code confusing because it won't run in the order you see, and that can make it hard to see order-dependent bugs.
+Member variables are always initialized in the order they are declared in the class definition, so write them in that order in the constructor initialization list. Writing them in a different order just makes the code confusing because it won't run in the order you see, and that can make it hard to see order-dependent bugs.
class Employee {
string email, first, last;
@@ -11824,7 +11824,7 @@ Member variables are always initialized in the order they are declared in the cl
Employee::Employee(const char* firstName, const char* lastName)
: first(firstName)
, last(lastName)
- , email(first + "." + last + "@acme.com") // BAD: first and last not yet constructed
+ , email(first + "." + last + "@acme.com") // BAD: first and last not yet constructed
{}
@@ -11834,7 +11834,7 @@ If the class definition and the constructor body are in separate files, the long
**References**
-[Cline99] §22.03-11  [Dewhurst03] §52-53  [Koenig97] §4  [Lakos96] §10.3.5  [Meyers97] §13  [Murray93] §2.1.3  [Sutter00] §47
+[Cline99] §22.03-11  [Dewhurst03] §52-53  [Koenig97] §4  [Lakos96] §10.3.5  [Meyers97] §13  [Murray93] §2.1.3  [Sutter00] §47
### Use of `=`, `{}`, and `()` as initializers
@@ -11886,11 +11886,11 @@ This design requires the following discipline:
* Allocation is limited to `operator new`. `B` can, however, override `new` (see Items 45 and 46).
* `D` must define a constructor with the same parameters that `B` selected. Defining several overloads of `Create` can assuage this problem, however; and the overloads can even be templated on the argument types.
-If the requirements above are met, the design guarantees that `PostInitialize` has been called for any fully constructed `B`-derived object. `PostInitialize` doesn't need to be virtual; it can, however, invoke virtual functions freely.
+If the requirements above are met, the design guarantees that `PostInitialize` has been called for any fully constructed `B`-derived object. `PostInitialize` doesn't need to be virtual; it can, however, invoke virtual functions freely.
-In summary, no post-construction technique is perfect. The worst techniques dodge the whole issue by simply asking the caller to invoke the post-constructor manually. Even the best require a different syntax for constructing objects (easy to check at compile time) and/or cooperation from derived class authors (impossible to check at compile time).
+In summary, no post-construction technique is perfect. The worst techniques dodge the whole issue by simply asking the caller to invoke the post-constructor manually. Even the best require a different syntax for constructing objects (easy to check at compile time) and/or cooperation from derived class authors (impossible to check at compile time).
-**References**: [Alexandrescu01] §3  [Boost]  [Dewhurst03] §75  [Meyers97] §46  [Stroustrup00] §15.4.3  [Taligent94]
+**References**: [Alexandrescu01] §3  [Boost]  [Dewhurst03] §75  [Meyers97] §46  [Stroustrup00] §15.4.3  [Taligent94]
@@ -11914,7 +11914,7 @@ class derived : public base { /*...*/ };
{
shared_ptr pb = make_shared();
- // ...
+ // ...
} // ~pb invokes correct destructor only when ~base is virtual
```
@@ -11935,7 +11935,7 @@ class customizable : Policy { /*...*/ }; // note: private inheritance
**Note**: This simple guideline illustrates a subtle issue and reflects modern uses of inheritance and object-oriented design principles.
-For a base class `Base`, calling code might try to destroy derived objects through pointers to `Base`, such as when using a `shared_ptr`. If `Base`'s destructor is public and nonvirtual (the default), it can be accidentally called on a pointer that actually points to a derived object, in which case the behavior of the attempted deletion is undefined. This state of affairs has led older coding standards to impose a blanket requirement that all base class destructors must be virtual. This is overkill (even if it is the common case); instead, the rule should be to make base class destructors virtual if and only if they are public.
+For a base class `Base`, calling code might try to destroy derived objects through pointers to `Base`, such as when using a `shared_ptr`. If `Base`'s destructor is public and nonvirtual (the default), it can be accidentally called on a pointer that actually points to a derived object, in which case the behavior of the attempted deletion is undefined. This state of affairs has led older coding standards to impose a blanket requirement that all base class destructors must be virtual. This is overkill (even if it is the common case); instead, the rule should be to make base class destructors virtual if and only if they are public.
To write a base class is to define an abstraction (see Items 35 through 37). Recall that for each member function participating in that abstraction, you need to decide:
@@ -11964,7 +11964,7 @@ In this rare case, you could make the destructor public and nonvirtual but clear
In general, however, avoid concrete base classes (see Item 35). For example, `unary_function` is a bundle-of-typedefs that was never intended to be instantiated standalone. It really makes no sense to give it a public destructor; a better design would be to follow this Item's advice and give it a protected nonvirtual destructor.
-**References**: [C++CS Item 50]; [Cargill92] pp. 77-79, 207 Ÿ [Cline99] §21.06, 21.12-13 Ÿ [Henricson97] pp. 110-114 Ÿ [Koenig97] Chapters 4, 11 Ÿ [Meyers97] §14 Ÿ [Stroustrup00] §12.4.2 Ÿ [Sutter02] §27 Ÿ [Sutter04] §18
+**References**: [C++CS Item 50]; [Cargill92] pp. 77-79, 207 Ÿ [Cline99] §21.06, 21.12-13 Ÿ [Henricson97] pp. 110-114 Ÿ [Koenig97] Chapters 4, 11 Ÿ [Meyers97] §14 Ÿ [Stroustrup00] §12.4.2 Ÿ [Sutter02] §27 Ÿ [Sutter04] §18
@@ -12071,9 +12071,9 @@ When using exceptions as your error handling mechanism, always document this beh
**Reason**: ???
-**Note**: If you define a copy constructor, you must also define a copy assignment operator.
+**Note**: If you define a copy constructor, you must also define a copy assignment operator.
-**Note**: If you define a move constructor, you must also define a move assignment operator.
+**Note**: If you define a move constructor, you must also define a move assignment operator.
**Example**:
@@ -12100,7 +12100,7 @@ If you define a destructor, you should not use the compiler-generated copy or mo
class X {
HANDLE hnd;
- // ...
+ // ...
public:
~X() { /* custom stuff, such as closing hnd */ }
@@ -12129,7 +12129,7 @@ If you define copying, and any base or member has a type that defines a move ope
x local;
// ...
return local; // pitfall: will be inefficient and/or do the wrong thing
- }
+ }
If you define any of the copy constructor, copy assignment operator, or destructor, you probably should define the others.
@@ -12139,9 +12139,9 @@ If you define any of the copy constructor, copy assignment operator, or destruct
* If you explicitly write the copying functions, you probably need to write the destructor: If the "special" work in the copy constructor is to allocate or duplicate some resource (e.g., memory, file, socket), you need to deallocate it in the destructor.
* If you explicitly write the destructor, you probably need to explicitly write or disable copying: If you have to write a nontrivial destructor, it's often because you need to manually release a resource that the object held. If so, it is likely that those resources require careful duplication, and then you need to pay attention to the way objects are copied and assigned, or disable copying completely.
-In many cases, holding properly encapsulated resources using RAII "owning" objects can eliminate the need to write these operations yourself. (See Item 13.)
+In many cases, holding properly encapsulated resources using RAII "owning" objects can eliminate the need to write these operations yourself. (See Item 13.)
-Prefer compiler-generated (including `=default`) special members; only these can be classified as "trivial," and at least one major standard library vendor heavily optimizes for classes having trivial special members. This is likely to become common practice.
+Prefer compiler-generated (including `=default`) special members; only these can be classified as "trivial," and at least one major standard library vendor heavily optimizes for classes having trivial special members. This is likely to become common practice.
**Exceptions**: When any of the special functions are declared only to make them nonpublic or virtual, but without special semantics, it doesn't imply that the others are needed.
In rare cases, classes that have members of strange types (such as reference members) are an exception because they have peculiar copy semantics.
@@ -12166,7 +12166,7 @@ Resource management rule summary:
### Provide strong resource safety; that is, never leak anything that you think of as a resource
-**Reason**: Prevent leaks. Leaks can lead to performance degradation, mysterious error, system crashes, and security violations.
+**Reason**: Prevent leaks. Leaks can lead to performance degradation, mysterious error, system crashes, and security violations.
**Alternative formulation**: Have every resource represented as an object of some class managing its lifetime.
@@ -12232,7 +12232,7 @@ The use of `array_view` and `string_view` should help a lot (they are not resour
### Never let a pointer outlive the object it points to
-**Reason**: To avoid extremely hard-to-find errors. Dereferencing such a pointer is undefined behavior and could lead to violations of the type system.
+**Reason**: To avoid extremely hard-to-find errors. Dereferencing such a pointer is undefined behavior and could lead to violations of the type system.
**Example**:
@@ -12299,7 +12299,7 @@ The `string`s of `v` are destroyed upon exit from `bad()` and so is `v` itself.
Now `Named` has a default constructor, a destructor, and efficient copy and move operations, provided `T` has.
-**Enforcement**: In general, a tool cannot know if a class is a resource handle. However, if a class has some of [the default operations](???), it should have all, and if a class has a member that is a resource handle, it should be considered a resource handle.
+**Enforcement**: In general, a tool cannot know if a class is a resource handle. However, if a class has some of [the default operations](???), it should have all, and if a class has a member that is a resource handle, it should be considered a resource handle.
### If a class is a container, give it an initializer-list constructor
@@ -12327,7 +12327,7 @@ Eventually, the entries will become rules or parts of rules.
Aternatively, we will decide that no change is needed and delete the entry.
* No long-distance friendship
-* Should physical design (what's in a file) and large-scale design (libraries, groups of libraries) be addressed?
+* Should physical design (what's in a file) and large-scale design (libraries, groups of libraries) be addressed?
* Namespaces
* How granular should namespaces be? All classes/functions designed to work together and released together (as defined in Sutter/Alexandrescu) or something narrower or wider?
* Should there be inline namespaces (a-la `std::literals::*_literals`)?
@@ -12341,8 +12341,8 @@ Aternatively, we will decide that no change is needed and delete the entry.
* Don't overabstract
* Never pass a pointer down the call stack
* falling through a function bottom
-* Should there be guidelines to choose between polymorphisms? YES. classic (virtual functions, reference semantics) vs. Sean Parent style (value semantics, type-erased, kind of like std::function) vs. CRTP/static? YES Perhaps even vs. tag dispatch?
-* Speaking of virtual functions, should non-virtual interface be promoted? NO. (public non-virtual foo() calling private/protected do_foo())? Not a new thing, seeing as locales/streams use it, but it seems to be under-emphasized.
+* Should there be guidelines to choose between polymorphisms? YES. classic (virtual functions, reference semantics) vs. Sean Parent style (value semantics, type-erased, kind of like std::function) vs. CRTP/static? YES Perhaps even vs. tag dispatch?
+* Speaking of virtual functions, should non-virtual interface be promoted? NO. (public non-virtual foo() calling private/protected do_foo())? Not a new thing, seeing as locales/streams use it, but it seems to be under-emphasized.
* should virtual calls be banned from ctors/dtors in your guidelines? YES. A lot of people ban them, even though I think it's a big strength of C++ that they are ??? -preserving (D disappointed me so much when it went the Java way). WHAT WOULD BE A GOOD EXAMPLE?
* Speaking of lambdas, what would weigh in on the decision between lambdas and (local?) classes in algorithm calls and other callback scenarios?
* And speaking of std::bind, Stephen T. Lavavej criticizes it so much I'm starting to wonder if it is indeed going to fade away in future. Should lambdas be recommended instead?