Discussion
It is crucial to crisply distinguish between errors and non-errors in terms of their effects on functions, especially for the purpose of defining safety guarantees (see Item 71). The key words in this Item are precondition, postcondition , and invariant .The function is the basic unit of work, no matter whether you are programming C++ in a structured style, an OO style, or a generic style. A function makes assumptions about its starting state (documented as its preconditions, which the caller is responsible for fulfilling and the callee is responsible for validating) and performs one or more actions (documented as its results or postconditions, which the function as callee is responsible for fulfilling). A function may share responsibility for maintaining one or more invariants. In particular, a nonprivate mutating member function is by definition a unit of work on its object, and must take the object from one valid invariant-preserving state to another; during the body of the member function, the object's invariants can be (and nearly always must be) broken, and that is fine and normal as long as they are reestablished by the end of the member function. Higher-level functions compose lower-level functions into larger units of work.An error is any failure that prevents a function from succeeding. There are three kinds:
- Violation of, or failure to achieve, a precondition:
The function detects a violation of one its own preconditions (e.g., a parameter or state restriction), or encounters a condition that prevents it from meeting a precondition of another essential function that must be called. - Failure to achieve a postcondition:
The function encounters a condition that prevents it from establishing one of its own postconditions. If the function has a return value, producing a valid return value object is a postcondition. - Failure to reestablish an invariant:
The function encounters a condition that prevents it from reestablishing an invariant that it is responsible for maintaining. This is a special kind of postcondition that applies particularly to member functions; an essential postcondition of every nonprivate member function is that it must reestablish its class's invariants. (See [Stroustrup00] §E.2.)
Any other condition is not an error and therefore should not be reported as an error. (See Examples.)The code that could cause an error is responsible for detecting and reporting the error. In particular, the caller should detect and report when it cannot meet a to-be-called function's preconditions (especially ones the callee documents that it will not check, such as vector::operator[] which does not promise to range-check its argument). Because the called function cannot rely on callers to be well-behaved, however, the called function ought still to validate its preconditions and to report violations by emitting an erroror, if the function is internal to (only callable from within) the module, so that any precondition violation is by definition an error in the module's programming, by asserting (see Item 68). This is defensive programming.Item 14).