@ -1778,7 +1778,7 @@ This is a major source of errors.
int printf(const char* ...); // bad: return negative number if output fails
template<classF,class...Args>
template<classF,class...Args>
// good: throw system_error if unable to start the new thread
explicit thread(F&& f, Args&&... args);
@ -2299,10 +2299,10 @@ So, we write a class
public:
enum Opt { from_line = 1 };
Istream() { }
Istream(zstring p) :owned{true}, inp{new ifstream{p}} {} // read from file
Istream(zstring p, Opt) :owned{true}, inp{new istringstream{p}} {} // read from command line
Istream(zstring p) :owned{true}, inp{new ifstream{p}} {} // read from file
Istream(zstring p, Opt) :owned{true}, inp{new istringstream{p}} {} // read from command line
~Istream() { if (owned) delete inp; }
operator istream&() { return *inp; }
operator istream&() { return *inp; }
private:
bool owned = false;
istream* inp = &cin;
@ -2958,7 +2958,8 @@ It's efficient and eliminates bugs at the call site: `X&&` binds to rvalues, whi
##### Example
void sink(vector<int>&& v) { // sink takes ownership of whatever the argument owned
void sink(vector<int>&& v) // sink takes ownership of whatever the argument owned
{
// usually there might be const accesses of v here
store_somewhere(std::move(v));
// usually no more use of v here; it is moved-from
@ -2974,8 +2975,9 @@ Unique owner types that are move-only and cheap-to-move, such as `unique_ptr`, c
For example:
template <classT>
void sink(std::unique_ptr<T> p) {
template<classT>
void sink(std::unique_ptr<T> p)
{
// use p ... possibly std::move(p) onward somewhere else
} // p gets destroyed
@ -2995,8 +2997,9 @@ In that case, and only that case, make the parameter `TP&&` where `TP` is a temp
##### Example
template <classF,class...Args>
inline auto invoke(F f, Args&&... args) {
template<classF,class...Args>
inline auto invoke(F f, Args&&... args)
{
return f(forward<Args>(args)...);
}
@ -3722,7 +3725,8 @@ This was primarily to avoid code of the form `(a = b) = c` -- such code is not c
{
public:
...
Foo& operator=(const Foo& rhs) {
Foo& operator=(const Foo& rhs)
{
// Copy members.
...
return *this;
@ -3779,7 +3783,7 @@ Functions can't capture local variables or be defined at local scope; if you nee
// at statement or expression scope -- a lambda is natural
vector<work> 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
@ -3795,7 +3799,7 @@ Generic lambdas offer a concise way to write function templates and so can be us
##### Enforcement
* Warn on use of a named non-generic lambda (e.g., `auto x = [](int i){ /*...*/; };`) that captures nothing and appears at global scope. Write an ordinary function instead.
* Warn on use of a named non-generic lambda (e.g., `auto x = [](int i){ /*...*/; };`) that captures nothing and appears at global scope. Write an ordinary function instead.
### <aname="Rf-default-args"></a>F.51: Where there is a choice, prefer default arguments over overloading
@ -3863,9 +3867,9 @@ This is a simple three-stage parallel pipeline. Each `stage` object encapsulates
@ -5642,7 +5649,8 @@ The return type of the factory should normally be `unique_ptr` by default; if so
class B {
public:
B() {
B()
{
/* ... */
f(); // BAD: C.82: Don't call virtual functions in constructors and destructors
/* ... */
@ -6173,7 +6181,8 @@ A *polymorphic class* is a class that defines or inherits at least one virtual f
// ...
};
void f(B& b) {
void f(B& b)
{
auto b2 = b; // oops, slices the object; b2.m() will return 'B'
}
@ -6196,7 +6205,8 @@ A *polymorphic class* is a class that defines or inherits at least one virtual f
// ...
};
void f(B& b) {
void f(B& b)
{
auto b2 = b; // ok, compiler will detect inadvertent copying, and protest
}
@ -6289,7 +6299,7 @@ In a few cases, a default operation is not desirable.
A `unique_ptr` can be moved, but not copied. To achieve that its copy operations are deleted. To avoid copying it is necessary to `=delete` its copy operations from lvalues:
template<classT,classD =default_delete<T>> class unique_ptr {
template<classT,classD =default_delete<T>> class unique_ptr {
public:
// ...
constexpr unique_ptr() noexcept;
@ -6498,7 +6508,7 @@ It is really hard to write a foolproof and useful `==` for a hierarchy.
`B`'s comparison accepts conversions for its second operand, but not its first.
class D :B {
class D :B {
char character;
virtual bool operator==(const D& a) const
{
@ -6572,14 +6582,12 @@ A type will provide a copy constructor and/or copy assignment operator to approp
##### Example, good
struct base
{
struct base {
virtual void update() = 0;
std::shared_ptr<int> sp;
};
struct derived : public base
{
struct derived : public base {
void update() override {}
};
@ -7137,7 +7145,7 @@ The importance of keeping the two kinds of inheritance increases
Using a value representing "uninitialized" is a symptom of a problem and not a solution:
@ -10877,10 +10884,10 @@ Readability and safety.
As an optimization, you may want to reuse a buffer as a scratch pad, but even then prefer to limit the variable's scope as much as possible and be careful not to cause bugs from data left in a recycled buffer as this is a common source of security bugs.
void write_to_file() {
void write_to_file()
{
std::string buffer; // to avoid reallocations on every loop iteration
for (auto& o : objects)
{
for (auto& o : objects) {
// First part of the work.
generate_first_string(buffer, o);
write_to_file(buffer);
@ -10957,7 +10964,7 @@ It nicely encapsulates local initialization, including cleaning up scratch varia
##### Example, good
const widget x = [&]{
const widget x = [&]{
widget val; // assume that widget has a default constructor
for (auto i = 2; i <= N; ++i) { // this could be some
val += some_obj.do_something_with(i); // arbitrarily long code
@ -10967,7 +10974,7 @@ It nicely encapsulates local initialization, including cleaning up scratch varia
##### Example
string var = [&]{
string var = [&]{
if (!in) return ""; // default
string s;
for (char c : in >> c)
@ -11161,7 +11168,7 @@ Requires messy cast-and-macro-laden code to get working right.
std::exit(severity);
}
template<typenameT,typename...Ts>
template<typenameT,typename...Ts>
constexpr void error(int severity, T head, Ts... tail)
{
std::cerr <<head;
@ -11804,11 +11811,13 @@ Sometimes, you may be tempted to resort to `const_cast` to avoid code duplicatio
class Foo {
public:
// BAD, duplicates logic
Bar& get_bar() {
Bar& get_bar()
{
/* complex logic around getting a non-const reference to my_bar */
}
const Bar& get_bar() const {
const Bar& get_bar() const
{
/* same complex logic around getting a const reference to my_bar */
}
private:
@ -11820,10 +11829,12 @@ Instead, prefer to share implementations. Normally, you can just have the non-`c
class Foo {
public:
// not great, non-const calls const version but resorts to const_cast
/* the complex logic around getting a const reference to my_bar */
}
private:
@ -12002,7 +12013,8 @@ Explicit `move` is needed to explicitly move an object to another scope, notably
Usually, a `std::move()` is used as an argument to a `&&` parameter.
And after you do that, assume the object has been moved from (see [C.64](#Rc-move-semantic)) and don't read its state again until you first set it to a new value.
void f() {
void f()
{
string s1 = "supercalifragilisticexpialidocious";
string s2 = s1; // ok, takes a copy
@ -12019,7 +12031,8 @@ And after you do that, assume the object has been moved from (see [C.64](#Rc-mov
void sink(unique_ptr<widget> p); // pass ownership of p to sink()
void f() {
void f()
{
auto w = make_unique<widget>();
// ...
sink(std::move(w)); // ok, give to sink()
@ -12039,7 +12052,8 @@ Never write `std::move()` on a const object, it is silently transformed into a c
##### Example, bad
vector<int> make_vector() {
vector<int> make_vector()
{
vector<int> result;
// ... load result with data
return std::move(result); // bad; just write "return result;"
@ -12058,14 +12072,16 @@ The language already knows that a returned value is a temporary object that can
##### Example
void mover(X&& x) {
void mover(X&& x)
{
call_something(std::move(x)); // ok
call_something(std::forward<X>(x)); // bad, don't std::forward an rvalue reference
call_something(x); // suspicious, why not std::move?
}
template<classT>
void forwarder(T&& t) {
void forwarder(T&& t)
{
call_something(std::move(t)); // bad, don't std::move a forwarding reference
call_something(std::forward<T>(t)); // ok
call_something(t); // suspicious, why not std::forward?
@ -12174,7 +12190,8 @@ In the rare cases where the slicing was deliberate the code can be surprising.
Shape s {c}; // copy construct only the Shape part of Circle
s = c; // or copy assign only the Shape part of Circle
void assign(const Shape& src, Shape& dest) {
void assign(const Shape& src, Shape& dest)
{
dest = src;
}
Circle c2 {{1, 1}, 43};
@ -12696,7 +12713,7 @@ consider `gsl::finally()` as a cleaner and more reliable alternative to `goto ex
##### Example
switch(x){
switch(x){
case 1 :
while (/* some condition */) {
//...
@ -12712,7 +12729,8 @@ consider `gsl::finally()` as a cleaner and more reliable alternative to `goto ex
Often, a loop that requires a `break` is a good candidate for a function (algorithm), in which case the `break` becomes a `return`.
//Original code: break inside loop
void use1(){
void use1()
{
std::vector<T> vec = {/* initialized with some values */};
T value;
for (const T item : vec) {
@ -12725,14 +12743,16 @@ Often, a loop that requires a `break` is a good candidate for a function (algori
}
//BETTER: create a function and return inside loop
T search(const std::vector<T>&vec) {
T search(const std::vector<T>&vec)
{
for (const T &item : vec) {
if (/* some condition*/) return item;
}
return T(); //default value
}
void use2() {
void use2()
{
std::vector<T> vec = {/* initialized with some values */};
T value = search(vec);
/* then do something with value */
@ -13251,20 +13271,23 @@ This also applies to `%`.
##### Example, bad
double divide(int a, int b) {
double divide(int a, int b)
{
// BAD, should be checked (e.g., in a precondition)
return a / b;
}
##### Example, good
double divide(int a, int b) {
double divide(int a, int b)
{
// good, address via precondition (and replace with contracts once C++ gets them)
Expects(b != 0);
return a / b;
}
double divide(int a, int b) {
double divide(int a, int b)
{
// good, address via check
return b ? a / b : quiet_NaN<double>();
}
@ -13500,8 +13523,7 @@ Simple code can be very fast. Optimizers sometimes do marvels with simple code
vector<uint8_t> v(100000);
for (size_t i = 0; i <v.size();i+=sizeof(uint64_t))
{
for (size_t i = 0; i <v.size();i+=sizeof(uint64_t)){
@ -19017,7 +19043,8 @@ Doing so takes away an `#include`r's ability to effectively disambiguate and to
bool copy(/*... some parameters ...*/); // some function that happens to be named copy
int main() {
int main()
{
copy(/*...*/); // now overloads local ::copy and std::copy, could be ambiguous
}
@ -21061,7 +21088,7 @@ Some styles distinguish members from local variable, and/or from global variable
struct S {
int m_;
S(int m) :m_{abs(m)} { }
S(int m) :m_{abs(m)} { }
};
This is not harmful and does not fall under this guideline because it does not encode type information.
@ -21722,7 +21749,8 @@ Here is an example of the last option:
class B {
public:
B() {
B()
{
/* ... */
f(); // BAD: C.82: Don't call virtual functions in constructors and destructors
/* ... */
@ -21868,7 +21896,7 @@ Never allow an error to be reported from a destructor, a resource deallocation f
class Nefarious {
public:
Nefarious() { /* code that could throw */ } // ok
Nefarious() { /* code that could throw */ } // ok
~Nefarious() { /* code that could throw */ } // BAD, should not throw
// ...
};
@ -21935,7 +21963,8 @@ Consider the following advice and requirements found in the C++ Standard:
Deallocation functions, including specifically overloaded `operator delete` and `operator delete[]`, fall into the same category, because they too are used during cleanup in general, and during exception handling in particular, to back out of partial work that needs to be undone.
Besides destructors and deallocation functions, common error-safety techniques rely also on `swap` operations never failing -- in this case, not because they are used to implement a guaranteed rollback, but because they are used to implement a guaranteed commit. For example, here is an idiomatic implementation of `operator=` for a type `T` that performs copy construction followed by a call to a no-fail `swap`: