This commit is contained in:
Sergey Zubkov
2019-08-19 11:35:36 -04:00
parent 4fdd7996aa
commit df145ab57e

View File

@@ -1,6 +1,6 @@
# <a name="main"></a>C++ Core Guidelines
May 2, 2019
June 16, 2019
Editors:
@@ -1055,8 +1055,8 @@ There are several more performance bugs and gratuitous complication.
for (int i = 0; i < strlen(s); ++i) s[i] = tolower(s[i]);
}
Yes, this is an example from production code.
We leave it to the reader to figure out what's wasted.
This is actually an example from production code.
We can see that in our condition we have `i < strlen(s)`. This expression will be evaluated on every iteration of the loop, which means that `strlen` must walk through string every loop to discover its length. While the string contents are changing, it's assumed that `toLower` will not affect the length of the string, so it's better to cache the length outside the loop and not incur that cost each iteration.
##### Note
@@ -1790,7 +1790,7 @@ If you can't use exceptions (e.g., because your code is full of old-style raw-po
// ... use val ...
This style unfortunately leads to uninitialized variables.
A facility [structured bindings](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0144r1.pdf) to deal with that will become available in C++17.
Since C++17 the "structured bindings" feature can be used to initialize variables directly from the return value:
auto [val, error_code] = do_something();
if (error_code) {
@@ -1962,8 +1962,8 @@ 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 when doing so, use `string_span` from the [GSL](#GSL) to prevent range errors.
Use `zstring` and `czstring` to represent C-style, zero-terminated strings.
But when doing so, use `std::string_view` or `string_span` from the [GSL](#GSL) to prevent range errors.
##### Enforcement
@@ -2176,10 +2176,6 @@ Different compilers implement different binary layouts for classes, exception ha
##### Exception
You can carefully craft an interface using a few carefully selected higher-level C++ types. See ???.
##### Exception
Common ABIs are emerging on some platforms freeing you from the more draconian restrictions.
##### Note
@@ -3312,6 +3308,8 @@ Passing a `span` object as an argument is exactly as efficient as passing a pair
C-style strings are ubiquitous. They are defined by convention: zero-terminated arrays of characters.
We must distinguish C-style strings from a pointer to a single character or an old-fashioned pointer to an array of characters.
If you don't need null termination, use `string_view`.
##### Example
Consider:
@@ -3746,7 +3744,7 @@ This should be enforced by tooling by checking the return expression .
##### Reason
Functions can't capture local variables or be declared at local scope; if you need those things, prefer a lambda where possible, and a handwritten function object where not. On the other hand, lambdas and function objects don't overload; if you need to overload, prefer a function (the workarounds to make lambdas overload are ornate). If either will work, prefer writing a function; use the simplest tool necessary.
Functions can't capture local variables or be defined at local scope; if you need those things, prefer a lambda where possible, and a handwritten function object where not. On the other hand, lambdas and function objects don't overload; if you need to overload, prefer a function (the workarounds to make lambdas overload are ornate). If either will work, prefer writing a function; use the simplest tool necessary.
##### Example
@@ -5490,7 +5488,7 @@ Copy and move constructors should not be made `explicit` because they do not per
##### Enforcement
(Simple) Single-argument constructors should be declared `explicit`. Good single argument non-`explicit` constructors are rare in most code based. Warn for all that are not on a "positive list".
(Simple) Single-argument constructors should be declared `explicit`. Good single argument non-`explicit` constructors are rare in most code bases. Warn for all that are not on a "positive list".
### <a name="Rc-order"></a>C.47: Define and initialize member variables in the order of member declaration
@@ -5598,7 +5596,7 @@ An initialization explicitly states that initialization, rather than assignment,
##### Example, better still
Instead of those `const char*`s we could use `gsl::string_span or (in C++17) `std::string_view`
Instead of those `const char*`s we could use `gsl::string_span` or (in C++17) `std::string_view`
as [a more general way to present arguments to a function](#Rstr-view):
class D { // Good
@@ -5622,49 +5620,55 @@ The return type of the factory should normally be `unique_ptr` by default; if so
class B {
public:
B()
{
// ...
f(); // BAD: virtual call in constructor
// ...
B() {
/* ... */
f(); // BAD: C.82: Don't call virtual functions in constructors and destructors
/* ... */
}
virtual void f() = 0;
// ...
};
##### Example
class B {
protected:
B() { /* ... */ } // create an imperfectly initialized object
virtual void PostInitialize() // to be called right after construction
{
// ...
f(); // GOOD: virtual dispatch is safe
// ...
}
class Token {};
public:
explicit B(Token) { /* ... */ } // create an imperfectly initialized object
virtual void f() = 0;
template<class T>
static shared_ptr<T> Create() // interface for creating shared objects
static shared_ptr<T> create() // interface for creating shared objects
{
auto p = make_shared<T>();
p->PostInitialize();
auto p = make_shared<T>(typename T::Token{});
p->post_initialize();
return p;
}
protected:
virtual void post_initialize() // called right after construction
{ /* ... */ f(); /* ... */ } // GOOD: virtual dispatch is safe
};
class D : public B { /* ... */ }; // some derived class
class D : public B { // some derived class
protected:
class Token {};
shared_ptr<D> p = D::Create<D>(); // creating a D object
public:
explicit D(Token) : B{ B::Token{} } {}
void f() override { /* ... */ };
By making the constructor `protected` we avoid an incompletely constructed object escaping into the wild.
By providing the factory function `Create()`, we make construction (on the free store) convenient.
protected:
template<class T>
friend shared_ptr<T> B::create();
};
shared_ptr<D> p = D::create<D>(); // creating a D object
`make_shared` requires that the constructor is public. By requiring a protected `Token` the constructor cannot be publicly called anymore, so we avoid an incompletely constructed object escaping into the wild.
By providing the factory function `create()`, we make construction (on the free store) convenient.
##### Note
@@ -5808,11 +5812,11 @@ But what if you can get significantly better performance by not making a tempora
{
if (a.sz > sz) {
// ... use the swap technique, it can't be bettered ...
return *this
return *this;
}
// ... copy sz elements from *a.elem to elem ...
if (a.sz < sz) {
// ... destroy the surplus elements in *this* and adjust size ...
// ... destroy the surplus elements in *this and adjust size ...
}
return *this;
}
@@ -5894,7 +5898,7 @@ After a copy `x` and `y` can be independent objects (value semantics, the way no
##### Note
Prefer copy semantics unless you are building a "smart pointer". Value semantics is the simplest to reason about and what the standard-library facilities expect.
Prefer value semantics unless you are building a "smart pointer". Value semantics is the simplest to reason about and what the standard-library facilities expect.
##### Enforcement
@@ -6305,6 +6309,7 @@ Worse, a direct or indirect call to an unimplemented pure virtual function from
virtual void f() = 0; // not implemented
virtual void g(); // implemented with Base version
virtual void h(); // implemented with Base version
virtual ~Base(); // implemented with Base version
};
class Derived : public Base {
@@ -6660,7 +6665,7 @@ resource management problems.
return sv;
}
A user can reasonably assume that returning a standard-like container is cheap.
A user can reasonably assume that returning a standard-like container is cheap.
##### Enforcement
@@ -6788,8 +6793,8 @@ Do *not* use inheritance when simply having a data member will do. Usually this
};
class PushButton : public AbstractButton {
virtual void render() const override;
virtual void onClick() override;
void render() const override;
void onClick() override;
// ...
};
@@ -6977,8 +6982,6 @@ It's simple and clear:
* `override` means exactly and only "this is a non-final overrider."
* `final` means exactly and only "this is a final overrider."
If a base class destructor is declared `virtual`, one should avoid declaring derived class destructors `virtual` or `override`. Some code base and tools might insist on `override` for destructors, but that is not the recommendation of these guidelines.
##### Example, bad
struct B {
@@ -6998,9 +7001,9 @@ If a base class destructor is declared `virtual`, one should avoid declaring der
##### Example, good
struct Better : B {
void f1(int) override; // error (caught): D::f1() hides B::f1()
void f1(int) override; // error (caught): Better::f1() hides B::f1()
void f2(int) const override;
void f3(double) override; // error (caught): D::f3() hides B::f3()
void f3(double) override; // error (caught): Better::f3() hides B::f3()
// ...
};
@@ -7260,7 +7263,7 @@ Copying a polymorphic class is discouraged due to the slicing problem, see [C.67
class B {
public:
virtual owner<B*> clone() = 0;
virtual ~B() = 0;
virtual ~B() = default;
B(const B&) = delete;
B& operator=(const B&) = delete;
@@ -7269,7 +7272,7 @@ Copying a polymorphic class is discouraged due to the slicing problem, see [C.67
class D : public B {
public:
owner<D*> clone() override;
virtual ~D() override;
~D() override;
};
Generally, it is recommended to use smart pointers to represent ownership (see [R.20](#Rr-owner)). However, because of language rules, the covariant return type cannot be a smart pointer: `D::clone` can't return a `unique_ptr<D>` while `B::clone` returns `unique_ptr<B>`. Therefore, you either need to consistently return `unique_ptr<B>` in all overrides, or use `owner<>` utility from the [Guidelines Support Library](#SS-views).
@@ -7545,6 +7548,7 @@ Without a using declaration, member functions in the derived class hide the enti
public:
virtual int f(int i) { std::cout << "f(int): "; return i; }
virtual double f(double d) { std::cout << "f(double): "; return d; }
virtual ~B() = default;
};
class D: public B {
public:
@@ -7632,6 +7636,7 @@ That can cause confusion: An overrider does not inherit default arguments.
class Base {
public:
virtual int multiply(int value, int factor = 2) = 0;
virtual ~Base() = default;
};
class Derived : public Base {
@@ -7659,7 +7664,7 @@ If you have a class with a virtual function, you don't (in general) know which c
##### Example
struct B { int a; virtual int f(); };
struct B { int a; virtual int f(); virtual ~B() = default };
struct D : B { int b; int f() override; };
void use(B b)
@@ -7687,6 +7692,10 @@ You can safely access a named polymorphic object in the scope of its definition,
d.f(); // OK
}
##### See also
[A polymorphic class should suppress copying](#Rc-copy-virtual)
##### Enforcement
Flag all slicing.
@@ -7702,6 +7711,7 @@ Flag all slicing.
struct B { // an interface
virtual void f();
virtual void g();
virtual ~B();
};
struct D : B { // a wider interface
@@ -7906,12 +7916,12 @@ Avoid resource leaks.
##### Reason
`make_unique` gives a more concise statement of the construction.
`make_unique` gives a more concise statement of the construction.
It also ensures exception safety in complex expressions.
##### Example
unique_ptr<Foo> p {new<Foo>{7}}; // OK: but repetitive
unique_ptr<Foo> p {new Foo{7}}; // OK: but repetitive
auto q = make_unique<Foo>(7); // Better: no repetition of Foo
@@ -7937,14 +7947,14 @@ It also ensures exception safety in complex expressions.
##### Reason
`make_shared` gives a more concise statement of the construction.
`make_shared` gives a more concise statement of the construction.
It also gives an opportunity to eliminate a separate allocation for the reference counts, by placing the `shared_ptr`'s use counts next to its object.
##### Example
void test() {
// OK: but repetitive; and separate allocations for the Bar and shared_ptr's use count
shared_ptr<Bar> p {new<Bar>{7}};
shared_ptr<Bar> p {new Bar{7}};
auto q = make_shared<Bar>(7); // Better: no repetition of Bar; one object
}
@@ -8475,7 +8485,7 @@ So far, so good, but we can easily misuse the `union`:
cout << v.x << '\n'; // BAD, undefined behavior: v holds a double, but we read it as an int
Note that the type error happened without any explicit cast.
When we tested that program the last value printed was `1683627180` which it the integer value for the bit pattern for `987.654`.
When we tested that program the last value printed was `1683627180` which is the integer value for the bit pattern for `987.654`.
What we have here is an "invisible" type error that happens to give a result that could easily look innocent.
And, talking about "invisible", this code produced no output:
@@ -8729,7 +8739,7 @@ Switching on an enumeration is common and the compiler can warn against unusual
}
}
Such off-by-one switch`statements are often the results of an added enumerator and insufficient testing.
Such off-by-one `switch`-statements are often the results of an added enumerator and insufficient testing.
##### Enforcement
@@ -9121,7 +9131,7 @@ Some interfaces cannot be simply annotated with `owner` because they need to rem
##### Note
`owner<T*>` has no default semantics beyond `T*`. It can be used without changing any code using it and without affecting ABIs.
It is simply a indicator to programmers and analysis tools.
It is simply an indicator to programmers and analysis tools.
For example, if an `owner<T*>` is a member of a class, that class better have a destructor that `delete`s it.
##### Example, bad
@@ -9219,7 +9229,7 @@ Instead, use a local variable:
##### Enforcement
* (Moderate) Warn if an object is allocated and then deallocated on all paths within a function. Suggest it should be a local `auto` stack object instead.
* (Simple) Warn if a local `Unique_ptr` or `Shared_ptr` is not moved, copied, reassigned or `reset` before its lifetime ends.
* (Simple) Warn if a local `Unique_pointer` or `Shared_pointer` is not moved, copied, reassigned or `reset` before its lifetime ends.
### <a name="Rr-global"></a>R.6: Avoid non-`const` global variables
@@ -9480,7 +9490,7 @@ This is more efficient:
##### 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.
(Simple) Warn if a function uses a `Shared_pointer` with an object allocated within the function, but never returns the `Shared_pointer` or passes it to a function requiring a `Shared_pointer&`. Suggest using `unique_ptr` instead.
### <a name="Rr-make_shared"></a>R.22: Use `make_shared()` to make `shared_ptr`s
@@ -9642,9 +9652,9 @@ Any type (including primary template or specialization) that overloads unary `*`
}
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;
`p` is a `Shared_pointer`, but nothing about its sharedness is used here and passing it by value is a silent pessimization;
these functions should accept a smart pointer only if they need to participate in the widget's lifetime management. Otherwise they should accept a `widget*`, if it can be `nullptr`. Otherwise, and ideally, the function should accept a `widget&`.
These smart pointers match the `Shared_ptr` concept, so these guideline enforcement rules work on them out of the box and expose this common pessimization.
These smart pointers match the `Shared_pointer` concept, so these guideline enforcement rules work on them out of the box and expose this common pessimization.
### <a name="Rr-uniqueptrparam"></a>R.32: Take a `unique_ptr<widget>` parameter to express that a function assumes ownership of a `widget`
@@ -9664,8 +9674,8 @@ Using `unique_ptr` in this way both documents and enforces the function call's o
##### Enforcement
* (Simple) Warn if a function takes a `Unique_ptr<T>` 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<T>` parameter by reference to `const`. Suggest taking a `const T*` or `const T&` instead.
* (Simple) Warn if a function takes a `Unique_pointer<T>` 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_pointer<T>` parameter by reference to `const`. Suggest taking a `const T*` or `const T&` instead.
### <a name="Rr-reseat"></a>R.33: Take a `unique_ptr<widget>&` parameter to express that a function reseats the`widget`
@@ -9687,8 +9697,8 @@ Using `unique_ptr` in this way both documents and enforces the function call's r
##### Enforcement
* (Simple) Warn if a function takes a `Unique_ptr<T>` 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<T>` parameter by reference to `const`. Suggest taking a `const T*` or `const T&` instead.
* (Simple) Warn if a function takes a `Unique_pointer<T>` 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_pointer<T>` parameter by reference to `const`. Suggest taking a `const T*` or `const T&` instead.
### <a name="Rr-sharedptrparam-owner"></a>R.34: Take a `shared_ptr<widget>` parameter to express that a function is part owner
@@ -9706,9 +9716,9 @@ This makes the function's ownership sharing explicit.
##### Enforcement
* (Simple) Warn if a function takes a `Shared_ptr<T>` 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<T>` 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<T>` by rvalue reference. Suggesting taking it by value instead.
* (Simple) Warn if a function takes a `Shared_pointer<T>` 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_pointer<T>` by value or by reference to `const` and does not copy or move it to another `Shared_pointer` on at least one code path. Suggest taking a `T*` or `T&` instead.
* (Simple) ((Foundation)) Warn if a function takes a `Shared_pointer<T>` by rvalue reference. Suggesting taking it by value instead.
### <a name="Rr-sharedptrparam"></a>R.35: Take a `shared_ptr<widget>&` parameter to express that a function might reseat the shared pointer
@@ -9730,9 +9740,9 @@ This makes the function's reseating explicit.
##### Enforcement
* (Simple) Warn if a function takes a `Shared_ptr<T>` 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<T>` 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<T>` by rvalue reference. Suggesting taking it by value instead.
* (Simple) Warn if a function takes a `Shared_pointer<T>` 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_pointer<T>` by value or by reference to `const` and does not copy or move it to another `Shared_pointer` on at least one code path. Suggest taking a `T*` or `T&` instead.
* (Simple) ((Foundation)) Warn if a function takes a `Shared_pointer<T>` by rvalue reference. Suggesting taking it by value instead.
### <a name="Rr-sharedptrparam-const"></a>R.36: Take a `const shared_ptr<widget>&` parameter to express that it might retain a reference count to the object ???
@@ -9750,9 +9760,9 @@ This makes the function's ??? explicit.
##### Enforcement
* (Simple) Warn if a function takes a `Shared_ptr<T>` 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<T>` 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<T>` by rvalue reference. Suggesting taking it by value instead.
* (Simple) Warn if a function takes a `Shared_pointer<T>` 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_pointer<T>` by value or by reference to `const` and does not copy or move it to another `Shared_pointer` on at least one code path. Suggest taking a `T*` or `T&` instead.
* (Simple) ((Foundation)) Warn if a function takes a `Shared_pointer<T>` by rvalue reference. Suggesting taking it by value instead.
### <a name="Rr-smartptrget"></a>R.37: Do not pass a pointer or reference obtained from an aliased smart pointer
@@ -9813,7 +9823,7 @@ The fix is simple -- take a local copy of the pointer to "keep a ref count" for
##### Enforcement
* (Simple) Warn if a pointer or reference obtained from a smart pointer variable (`Unique_ptr` or `Shared_ptr`) that is nonlocal, or that is local but potentially aliased, is used in a function call. If the smart pointer is a `Shared_ptr` then suggest taking a local copy of the smart pointer and obtain a pointer or reference from that instead.
* (Simple) Warn if a pointer or reference obtained from a smart pointer variable (`Unique_pointer` or `Shared_pointer`) that is nonlocal, or that is local but potentially aliased, is used in a function call. If the smart pointer is a `Shared_pointer` then suggest taking a local copy of the smart pointer and obtain a pointer or reference from that instead.
# <a name="S-expr"></a>ES: Expressions and statements
@@ -9907,7 +9917,7 @@ Arithmetic rules:
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 known and best tested libraries.
It is available as part of all C++ Implementations.
It is available as part of all C++ implementations.
##### Example
@@ -10719,7 +10729,7 @@ Use `={...}` if you really want an `initializer_list<T>`
`={}` gives copy initialization whereas `{}` gives direct initialization.
Like the distinction between copy-initialization and direct-initialization itself, this can lead to surprises.
`{}` accepts `explicit` constructors; `={}` does not`. For example:
`{}` accepts `explicit` constructors; `={}` does not. For example:
struct Z { explicit Z() {} };
@@ -10822,7 +10832,7 @@ As an optimization, you may want to reuse a buffer as a scratch pad, but even th
for (auto& o : objects)
{
// First part of the work.
generate_first_String(buffer, o);
generate_first_string(buffer, o);
write_to_file(buffer);
// Second part of the work.
@@ -11092,6 +11102,7 @@ Requires messy cast-and-macro-laden code to get working right.
}
**Alternative**: Overloading. Templates. Variadic templates.
#include <iostream>
void error(int severity)
@@ -11553,16 +11564,16 @@ We also include lossy arithmetic casts, such as from a negative floating point t
unsigned u = 0;
u = d; // BAD
u = narrow_cast<unsigned>(d); // OK (you asked for it): u becomes 0
u = narrow_cast<unsigned>(d); // OK (you asked for it): u becomes 4294967289
u = narrow<unsigned>(d); // OK: throws narrowing_error
##### Enforcement
A good analyzer can detect all narrowing conversions. However, flagging all narrowing conversions will lead to a lot of false positives. Suggestions:
* flag all floating-point to integer conversions (maybe only `float`->`char` and `double`->`int`. Here be dragons! we need data)
* flag all `long`->`char` (I suspect `int`->`char` is very common. Here be dragons! we need data)
* consider narrowing conversions for function arguments especially suspect
* Flag all floating-point to integer conversions (maybe only `float`->`char` and `double`->`int`. Here be dragons! we need data).
* Flag all `long`->`char` (I suspect `int`->`char` is very common. Here be dragons! we need data).
* Consider narrowing conversions for function arguments especially suspect.
### <a name="Res-nullptr"></a>ES.47: Use `nullptr` rather than `0` or `NULL`
@@ -11646,11 +11657,11 @@ Casts are widely (mis) used. Modern C++ has rules and constructs that eliminate
##### Enforcement
* Force the elimination of C-style casts, except on a function with a `[[nodiscard]]` return
* Warn if there are many functional style casts (there is an obvious problem in quantifying 'many')
* Force the elimination of C-style casts, except on a function with a `[[nodiscard]]` return.
* Warn if there are many functional style casts (there is an obvious problem in quantifying 'many').
* The [type profile](#Pro-type-reinterpretcast) bans `reinterpret_cast`.
* Warn against [identity casts](#Pro-type-identitycast) between pointer types, where the source and target types are the same (#Pro-type-identitycast)
* Warn if a pointer cast could be [implicit](#Pro-type-implicitpointercast)
* Warn against [identity casts](#Pro-type-identitycast) between pointer types, where the source and target types are the same (#Pro-type-identitycast).
* Warn if a pointer cast could be [implicit](#Pro-type-implicitpointercast).
### <a name="Res-casts-named"></a>ES.49: If you must use a cast, use a named cast
@@ -12316,7 +12327,7 @@ There are two potential problems with testing for `nullptr`:
* the test can be redundant and/or relatively expensive
* it is not obvious if the test is to protect against a violation or part of the required logic.
<!-- comment needed for code block after list -->
void f2(int* p) // state that p is not supposed to be nullptr
{
assert(p);
@@ -13221,7 +13232,7 @@ For example
##### Enforcement
Hard: there is a lot of code using `unsigned` and we don't offer a practical positive number type.
See ES.100 Enforcements.
### <a name="Res-subscripts"></a>ES.107: Don't use `unsigned` for subscripts, prefer `gsl::index`
@@ -15641,7 +15652,7 @@ Instead, use a reference:
catch (exception& e) { /* ... */ }
of - typically better still - a `const` reference:
or - typically better still - a `const` reference:
catch (const exception& e) { /* ... */ }
@@ -15729,7 +15740,7 @@ Let cleanup actions on the unwinding path be handled by [RAII](#Re-raii).
##### Reason
`try`/`catch` is verbose and non-trivial uses error-prone.
`try`/`catch` is verbose and non-trivial uses are error-prone.
`try`/`catch` can be a sign of unsystematic and/or low-level resource management or error handling.
##### Example, Bad
@@ -15914,8 +15925,8 @@ Systematic use of any error-handling strategy minimizes the chance of forgetting
There are several issues to be addressed:
* how do you transmit an error indicator from out of a function?
* how do you release all resources from a function before doing an error exit?
* How do you transmit an error indicator from out of a function?
* How do you release all resources from a function before doing an error exit?
* What do you use as an error indicator?
In general, returning an error indicator implies returning two values: The result and an error indicator.
@@ -16366,7 +16377,7 @@ In C++, these requirements are expressed by compile-time predicates called conce
Templates can also be used for meta-programming; that is, programs that compose code at compile time.
A central notion in generic programming is "concepts"; that is, requirements on template arguments presented as compile-time predicates.
"Concepts" are defined in an ISO Technical specification: [concepts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf).
"Concepts" are defined in an ISO Technical Specification: [concepts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf).
A draft of a set of standard-library concepts can be found in another ISO TS: [ranges](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf)
Concepts are supported in GCC 6.1 and later.
Consequently, we comment out uses of concepts in examples; that is, we use them as formalized comments only.
@@ -16524,7 +16535,7 @@ is to efficiently generalize operations/algorithms over a set of types with simi
##### Note
The `requires` in the comments are uses of `concepts`.
"Concepts" are defined in an ISO Technical specification: [concepts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf).
"Concepts" are defined in an ISO Technical Specification: [concepts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf).
Concepts are supported in GCC 6.1 and later.
Consequently, we comment out uses of concepts in examples; that is, we use them as formalized comments only.
If you use GCC 6.1 or later, you can uncomment them.
@@ -16659,7 +16670,7 @@ See the reference to more specific rules.
## <a name="SS-concepts"></a>T.concepts: Concept rules
Concepts is a facility for specifying requirements for template arguments.
It is an [ISO technical specification](#Ref-conceptsTS), but currently supported only by GCC.
It is an [ISO Technical Specification](#Ref-conceptsTS), but currently supported only by GCC.
Concepts are, however, crucial in the thinking about generic programming and the basis of much work on future C++ libraries
(standard and other).
@@ -16716,7 +16727,7 @@ or equivalently and more succinctly:
##### Note
"Concepts" are defined in an ISO Technical specification: [concepts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf).
"Concepts" are defined in an ISO Technical Specification: [concepts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf).
A draft of a set of standard-library concepts can be found in another ISO TS: [ranges](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf)
Concepts are supported in GCC 6.1 and later.
Consequently, we comment out uses of concepts in examples; that is, we use them as formalized comments only.
@@ -16821,7 +16832,7 @@ The shorter versions better match the way we speak. Note that many templates don
##### Note
"Concepts" are defined in an ISO Technical specification: [concepts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf).
"Concepts" are defined in an ISO Technical Specification: [concepts](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf).
A draft of a set of standard-library concepts can be found in another ISO TS: [ranges](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf)
Concepts are supported in GCC 6.1 and later.
Consequently, we comment out uses of concepts in examples; that is, we use them as formalized comments only.
@@ -17528,7 +17539,19 @@ Because that's the best we can do without direct concept support.
##### Example
enable_if<???>
template <typename T>
enable_if_t<is_integral_v<T>>
f(T v)
{
// ...
}
// Equivalent to:
template <Integral T>
void f(T v)
{
// ...
}
##### Note
@@ -17665,8 +17688,8 @@ Typically, the solution is to make what would have been a nested class non-local
List<int, My_allocator> lst2;
Some people found the idea that the `Link` no longer was hidden inside the list scary, so we named the technique
[SCARY](http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2009/n2911.pdf).From that academic paper:
"The acronym SCARY describes assignments and initializations that are Seemingly erroneous (appearing Constrained by conflicting generic parameters), but Actually work with the Right implementation (unconstrained bY the conflict due to minimized dependencies."
[SCARY](http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2009/n2911.pdf). From that academic paper:
"The acronym SCARY describes assignments and initializations that are Seemingly erroneous (appearing Constrained by conflicting generic parameters), but Actually work with the Right implementation (unconstrained bY the conflict due to minimized dependencies)."
##### Enforcement
@@ -17826,7 +17849,7 @@ When `concept`s become widely available such alternatives can be distinguished d
template<typename T, typename U>
void f(T t, U u)
{
T v1(x); // is v1 a function of a variable?
T v1(x); // is v1 a function or a variable?
T v2 {x}; // variable
auto x = T(u); // construction or cast?
}
@@ -18871,6 +18894,12 @@ Doing so takes away an `#include`r's ability to effectively disambiguate and to
copy(/*...*/); // now overloads local ::copy and std::copy, could be ambiguous
}
##### Note
An exception is `using namespace std::literals;`. This is necessary to use string literals
in header files and given [the rules](http://eel.is/c++draft/over.literal) - users are required
to name their own UDLs `operator""_x` - they will not collide with the standard library.
##### Enforcement
Flag `using namespace` at global scope in a header file.
@@ -18982,7 +19011,6 @@ For example:
// basic_std_lib.h:
#include <vector>
#include <string>
#include <map>
#include <iostream>
@@ -19017,6 +19045,10 @@ Avoid clients of a header having to manage that header's dependencies.
Failing to follow this results in difficult to diagnose errors for clients of a header.
##### Note
A header should include all its dependencies. Be careful about using relative paths because C++ implementations diverge on their meaning.
##### Enforcement
A test should verify that the header file itself compiles or that a cpp file which only includes the header file compiles.
@@ -19202,7 +19234,7 @@ People working with code for which that difference matters are quite capable of
Usually you need to add and remove elements from the container, so use `vector` by default; if you don't need to modify the container's size, use `array`.
Even when other containers seem more suited, such a `map` for O(log N) lookup performance or a `list` for efficient insertion in the middle, a `vector` will usually still perform better for containers up to a few KB in size.
Even when other containers seem more suited, such as `map` for O(log N) lookup performance or a `list` for efficient insertion in the middle, a `vector` will usually still perform better for containers up to a few KB in size.
##### Note
@@ -19321,7 +19353,7 @@ The important issue of non-ASCII character sets and encodings (e.g., `wchar_t`,
**See also**: [regular expressions](#SS-regex)
Here, we use "sequence of characters" or "string" to refer to a sequence of characters meant to be read as text (somehow, eventually).
We don't consider
We don't consider ???
String summary:
@@ -19403,7 +19435,7 @@ Will this code pass a security review?
##### Note
Do not assume that `string` is slower than lower-level techniques without measurement and remember than not all code is performance critical.
Do not assume that `string` is slower than lower-level techniques without measurement and remember that not all code is performance critical.
[Don't optimize prematurely](#Rper-Knuth)
##### Enforcement
@@ -19678,8 +19710,9 @@ implicit memory management, and `locale` handling.
If you need I/O performance, you can almost always do better than `printf()`.
`gets()` `scanf()` using `s`, and `printf()` using `%s` are security hazards (vulnerable to buffer overflow and generally error-prone).
In C11, they are replaced by `gets_s()`, `scanf_s()`, and `printf_s()` as safer alternatives, but they are still not type safe.
`gets()`, `scanf()` using `%s`, and `printf()` using `%s` are security hazards (vulnerable to buffer overflow and generally error-prone).
C11 defines some "optional extensions" that do extra checking of their arguments.
If present in your C library, `gets_s()`, `scanf_s()`, and `printf_s()` may be safer alternatives, but they are still not type safe.
##### Enforcement
@@ -20062,7 +20095,7 @@ and errors (when we didn't deal correctly with semi-constructed objects consiste
if (data) {
return false;
}
data = (char*) malloc(x*y*sizeof(int));
data = (char*) malloc(mx*my*sizeof(int));
return data != nullptr;
}
@@ -20613,9 +20646,6 @@ Most of the concepts below are defined in [the Ranges TS](http://www.open-std.or
* `String` // ???
* `Number` // ???
* `Sortable`
* `Pointer` // A type with `*`, `->`, `==`, and default construction (default construction is assumed to set the singular "null" value); see [smart pointers](#SS-gsl-smartptrconcepts)
* `Unique_ptr` // A type that matches `Pointer`, has move (not copy), and matches the Lifetime profile criteria for a `unique` owner type; see [smart pointers](#SS-gsl-smartptrconcepts)
* `Shared_ptr` // A type that matches `Pointer`, has copy, and matches the Lifetime profile criteria for a `shared` owner type; see [smart pointers](#SS-gsl-smartptrconcepts)
* `EqualityComparable` // ???Must we suffer CaMelcAse???
* `Convertible`
* `Common`
@@ -20633,7 +20663,9 @@ Most of the concepts below are defined in [the Ranges TS](http://www.open-std.or
### <a name="SS-gsl-smartptrconcepts"></a>GSL.ptr: Smart pointer concepts
See [Lifetime paper](https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/Lifetime.pdf).
* `Pointer` // A type with `*`, `->`, `==`, and default construction (default construction is assumed to set the singular "null" value)
* `Unique_pointer` // A type that matches `Pointer`, is movable, and is not copyable
* `Shared_pointer` // A type that matches `Pointer`, and is copyable
# <a name="S-naming"></a>NL: Naming and layout rules
@@ -21473,51 +21505,64 @@ Here is an example of the last option:
class B {
public:
B() { /* ... */ f(); /* ... */ } // BAD: see Item 49.1
B() {
/* ... */
f(); // BAD: C.82: Don't call virtual functions in constructors and destructors
/* ... */
}
virtual void f() = 0;
// ...
};
class B {
protected:
B() { /* ... */ }
virtual void post_initialize() // called right after construction
{ /* ... */ f(); /* ... */ } // GOOD: virtual dispatch is safe
class Token {};
public:
// constructor needs to be public so that make_shared can access it.
// protected access level is gained by requiring a Token.
explicit B(Token) { /* ... */ } // create an imperfectly initialized object
virtual void f() = 0;
template<class T>
static shared_ptr<T> create() // interface for creating objects
static shared_ptr<T> create() // interface for creating shared objects
{
auto p = make_shared<T>();
auto p = make_shared<T>(typename T::Token{});
p->post_initialize();
return p;
}
protected:
virtual void post_initialize() // called right after construction
{ /* ... */ f(); /* ... */ } // GOOD: virtual dispatch is safe
}
};
class D : public B { // some derived class
protected:
class Token {};
public:
// constructor needs to be public so that make_shared can access it.
// protected access level is gained by requiring a Token.
explicit D(Token) : B{ B::Token{} } {}
void f() override { /* ... */ };
protected:
D() {}
template<class T>
friend shared_ptr<T> B::Create();
friend shared_ptr<T> B::create();
};
shared_ptr<D> p = D::Create<D>(); // creating a D object
shared_ptr<D> p = D::create<D>(); // creating a D object
This design requires the following discipline:
* Derived classes such as `D` must not expose a public constructor. Otherwise, `D`'s users could create `D` objects that don't invoke `PostInitialize`.
* 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.
* Derived classes such as `D` must not expose a publicly callable constructor. Otherwise, `D`'s users could create `D` objects that don't invoke `post_initialize`.
* Allocation is limited to `operator new`. `B` can, however, override `new` (see Items 45 and 46 in [SuttAlex05](#SuttAlex05)).
* `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 `post_initialize` has been called for any fully constructed `B`-derived object. `post_initialize` 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).