@ -3911,11 +3913,20 @@ Leaving behind an invalid object is asking for trouble.
##### Note
For a variable definition (e.g., on the stack or as a member of another object) there is no explicit function call from which an error code could be returned. Leaving behind an invalid object an relying on users to consistently check an `is_valid()` function before use is tedious, error-prone, and inefficient.
For a variable definition (e.g., on the stack or as a member of another object) there is no explicit function call from which an error code could be returned.
Leaving behind an invalid object an relying on users to consistently check an `is_valid()` function before use is tedious, error-prone, and inefficient.
**Exception**: There are domains, such as some hard-real-time systems (think airplane controls) where (without additional tool support) exception handling is not sufficiently predictable from a timing perspective. There the `is_valid()` technique must be used. In such cases, check `is_valid()` consistently and immediately to simulate [RAII](#Rr-raii).
**Exception**: There are domains, such as some hard-real-time systems (think airplane controls) where (without additional tool support) exception handling is not sufficiently predictable from a timing perspective.
There the `is_valid()` technique must be used. In such cases, check `is_valid()` consistently and immediately to simulate [RAII](#Rr-raii).
**Alternative**: If you feel tempted to use some "post-constructor initialization" or "two-stage initialization" idiom, try not to do that. If you really have to, look at [factory functions](#Rc-factory).
**Alternative**: If you feel tempted to use some "post-constructor initialization" or "two-stage initialization" idiom, try not to do that.
If you really have to, look at [factory functions](#Rc-factory).
##### Note
One reason people have used `init()` functions rather than doing the initialization work in a constructor has been to avoid code replication.
[Delegating constructors](#Rc-delegating) and [default member initialization](#Rc-in-class-initializer) do that better.
Another reason is been to delay initialization until an object is needed; the solution to that is often [not to declare a variable until it can be properly initialized](#Res-init)
##### Enforcement
@ -5354,7 +5365,7 @@ This kind of "vector" isn't meant to be used as a base class at all.
##### Reason
`protected` data is a source of complexity and errors.
`protected` data is a source of complexity and errors.
`protected` data complicated the statement of invariants.
`protected` data inherently violates the guidance against putting data in base classes, which usually leads to having to deal virtual inheritance as well.
@ -5374,7 +5385,19 @@ Flag classes with `protected` data.
##### Reason
If they don't, the type is confused about what it's trying to do. Only if the type is not really an abstraction, but just a convenience bundle to group individual variables with no larger behavior (a behaviorless bunch of variables), make all data members `public` and don't provide functions with behavior. Otherwise, the type is an abstraction, so make all its data members `private`. Don't mix `public` and `private` data.
Prevention of logical confusion leading to errors.
If the data members don't have the same access level, the type is confused about what it's trying to do.
Is it a type that maintains an invariant os simply a collection of values?
##### Note
This leaves us with three alternatives:
* *All public*: If you're writing an aggregate bundle-of-variables without an invariant across those variables, then all the variables should be public.
[Ddeclare such classes `struct` rather than `class`](#Rc-struct)
int webby = blue; // error, ambiguous: be specific
Webcolor webby = Webcolor::blue;
instead use an `enum class`:
enum class Webcolor { red=0xFF0000, green=0x00FF00, blue=0x0000FF };
enum class Productinfo { red=0, purple=1, blue=2 };
int webby = blue; // error: blue undefined in this scope
Webcolor webby = Webcolor::blue;
##### Enforcement
@ -6871,7 +6927,7 @@ Statement rules:
* [ES.75: Avoid `do`-statements](#Res-do)
* [ES.76: Avoid `goto`](#Res-goto)
* [ES.77: ??? `continue`](#Res-continue)
* [ES.78: ??? `break`](#Res-break)
* [ES.78: Always end non-empty a `case` with a `break`](#Res-break)
* [ES.79: ??? `default`](#Res-default)
* [ES.85: Make empty statements visible](#Res-empty)
@ -7248,6 +7304,8 @@ Flag redundant repetition of type names in a declaration.
##### Reason
Avoid used-before-set errors and their associated undefined behavior.
Avoid problems with comprehension of complex initialization.
Simplify refactoring.
##### Example
@ -7267,13 +7325,81 @@ No, `i = 7` does not initialize `i`; it assigns to it. Also, `i` can be read in
// ...
}
##### Note
The *always initialize* rule is deliberately stronger than the *an object must be set before used* language rule.
The latter, more relaxed rule, catches the technical bugs, but:
* It leads to less readable code
* It encourages people to declare names in greater than necessary scopes
* It leads to harder to read code
* It leads to logic bugs by encouraging complex code
* It hampers refactoring
The *always initialize* rule is a style rule aimed to improve maintainability as well as a rule protecting against used-before-set errors.
##### Example
Here is an example that is often considered to demonstrate the need for a more relaxed rule for initialization
widget i, j; // "widget" a type that's expensive to initialize, possibly a large POD
if (cond) { // bad: i and j are initialized "late"
i = f1();
j = f2();
}
else {
i = f3();
j = f4();
}
This cannot trivially be rewritten to initialize `i` and `j` with initializers.
Note that for types with a default constructor, attempting to postpone initialization simply leads to a default initialization followed by an assignment.
A popular reason for such examples is "efficiency", but a compiler that can detect whether we made a used-before-set error can also eliminate any redundant double initialization.
At the cost of repeating `cond` we could write
widget i = (cond) ? f1() : f3();
widget j = (cond) ? f2() : f4();
Assuming that there is a logical connection between `i` and `j`, that connection should probably be expressed in code:
pair<widget,widget> make_related_widgets(bool x)
{
return (x) ? {f1(),f2()} : {f3(),f4() };
}
auto init = make_related_widgets(cond);
widget i = init.first;
widget j = init.second;
Obviously, what we really would like is a construct that initialized n variables from a `tuple`. For example:
auto {i,j} = make_related_widgets(cond); // Not C++14
Today, we might approximate that using `tie()`:
widget i; // bad: uninitialized variable
widget j;
tie(i,j) = make_related_widgets(cond);
This may be seen as an example of the *immediately initialize from input* exception below.
Creating optimal and equivalent code from all of these examples should be well within the capabilities of modern C++ compilers.
##### Note
Complex initialization has been popular with clever programmers for decades.
It has also been a major source of errors and complexity.
Many such errors are introduced during maintenance years after the initial implementation.
##### Exception
It you are declaring an object that is just about to be initialized from input, initializing it would cause a double initialization.
However, beware that this may leave uninitialized data beyond the input - and that has been a fertile source of errors and security breaches:
constexpr int max = 8*1024;
int buf[max]; // OK, but suspicious
int buf[max]; // OK, but suspicious: uninitialized
f.read(buf, max);
The cost of initializing that array could be significant in some situations.
@ -7283,10 +7409,10 @@ However, such examples do tend to leave uninitialized variables accessible, so t
int buf[max] = {0}; // better in some situations
f.read(buf, max);
When feasible use a library function that is know not to overflow. For example:
When feasible use a library function that is known not to overflow. For example:
string s; // s is default initialized to ""
cin >> s; // s expands to hold the string
cin >> s; // s expands to hold the string
Don't consider simple variables that are targets for input operations exceptions to this rule:
@ -7302,41 +7428,55 @@ In the not uncommon case where the input target and the input operation get sepa
A good optimizer should know about input operations and eliminate the redundant operation.
##### Exception
##### Example:
Sometimes, we want to initialize a set of variables with a call to a function that returns several values.
That can lead to uninitialized variables (exceptly as for input operations):
Using an `unitialized` value is a symptom of a problem and not a solution:
error_code ec;
Value v;
tie(ec, v) = get_value(); // get_value() returns a pair<error_code,Value>
widget i = uninit; // bad
widget j = uninit;
// ...
use(i); // possibly used before set
// ...
if (cond) { // bad: i and j are initialized "late"
i = f1();
j = f2();
}
else {
i = f3();
j = f4();
}
Now the compiler cannot even simply detect a used-befor-set.
##### Note
Sometimes, a lambda can be used as an initializer to avoid an uninitialized variable.
error_code ec;
Value v = [&]() {
Value v = [&] {
auto p = get_value(); // get_value() returns a pair<error_code,Value>
ec = p.first;
return p.second;
};
}();
or maybe:
Value v = []() {
Value v = [] {
auto p = get_value(); // get_value() returns a pair<error_code,Value>
if (p.first) throw Bad_value{p.first};
return p.second;
};
}();
**See also**: [ES.28](#Res-lambda-init)
##### Enforcement
* Flag every uninitialized variable.
Don't flag variables of user-defined types with default constructors.
* Check that the uninitialized buffer is read into *immediately* after declaration.
Don't flag variables of user-defined types with default constructors.
* Check that an uninitialized buffer is written into *immediately* after declaration.
Passing a uninitialized variable as a non-`const` reference argument can be assumed to be a write into the variable.
### <aname="Res-introduce"></a> ES.21: Don't introduce a variable (or constant) before you need to use it
@ -7930,15 +8070,43 @@ This is an ad-hoc simulation of destructors. Declare your resources with handles
???
### <aname="Res-break"></a> ES.78: Always end a `case` with a `break`
### <aname="Res-break"></a> ES.78: Always end non-empty a `case` with a `break`
##### Reason
??? loop, switch ???
Accidentally leaving out a `break` is a fairly common bug.
A deliberate fallthrough is a maintenance hazard.
##### Example
???
switch(eventType)
{
case Information:
update_status_bar();
break;
case Warning:
write_event_log();
case Error:
display_error_window(); // Bad
break;
}
It is easy to overlook the fallthrough. Be explicit:
switch(eventType)
{
case Information:
update_status_bar();
break;
case Warning:
write_event_log();
// fall through
case Error:
display_error_window(); // Bad
break;
}
There is a proposal for a `[[fallthrough]]` annotation.
##### Note
@ -7954,7 +8122,7 @@ Multiple case labels of a single statement is OK: