Range Checking in Vector Operations
The word range-checked describes the behavior you would expect: the vector verifies that an index falls within the valid range of positions before accessing an element.
Try it: try / except / else / finally
How an exception jumps out of a try block to the first matching except clause, when else runs, and why finally always runs.
How it works
- Statements in try run until one raises an exception; the rest of try is skipped.
- Python checks the except clauses in order and runs the first one whose type matches.
- If nothing was raised, the else block runs instead.
- finally runs in every case.
Default run (9 steps): Call safe_divide('4'). … Return 25.0. Output was: Worked: 25.0 / Done.
Simplified: One fixed function; int() and / are modelled exactly (including how Python prints floats), but nothing is executed as Python.
Loading the simulation…
When Nothing Catches the Error
Suppose a vector operation fails in the middle of a long program. Somewhere in the code, an exception has been thrown to signal that something went wrong, but no catch clause anywhere in the program is written to receive it. What should the program do with that error? Should the failure be silent, should it be printed somewhere, or should something else happen? Decide on a sensible default before reading on, because that is exactly the question the default exception handler answers.
The source describes the rule in one sentence: an exception that no handler catches reaches a default exception handler. This article follows one thrown exception from the point where it is raised to the point where the default handler reports it, and explains why that default matters even when you have written careful handling code elsewhere.
How the Runtime Searches for a Handler
When code throws an exception, control does not simply continue to the next line. The runtime looks for a handler that matches the kind of exception that was thrown. If a matching catch clause exists, that handler takes over and the program can deal with the problem in its own way. If no matching handler exists, the exception keeps going unhandled, and the default behavior provided for this situation takes effect.
A throw is a request for a handler, not a command to print something. Whether anything is printed depends on whether a matching handler exists somewhere along the path the exception takes.
Unwinding Through Nested Calls
An exception may be thrown deep inside nested function calls, and the code that threw it may have no idea who will eventually handle it. The exception therefore travels outward through the chain of calls that led to the throw. At each level, the runtime checks whether a handler is present. Only when the chain is exhausted with no matching handler does the exception reach the default behavior.
In the diagram, the exception starts in check_index and moves outward one call at a time: first into read_slot, then into main. Each function in the chain is left at the point of the throw, so the statements after the failing call in those functions never run. If main had a matching handler, the exception would stop there. Because the source places the default handler around main(), an exception that reaches main without a match is reported at that single outermost point.
The Default Handler and cerr
The default handler is the behavior that takes effect when an exception is not caught. It prints an error message on cerr, the standard error-diagnostic output stream. The default handler ensures that, whenever an exception is not caught, an error message appears on cerr.
What do you think happens?
When an exception thrown in a nested call is never caught, where does the default error message go?
Reveal answer
cerr, the standard error-diagnostic stream
The default handler prints its error message on cerr. This is the reporting path a learner can rely on without writing a handler for every possible failure.
The key point is that a learner does not have to remember to write a handler for every possible failure in order to get some visible report of it. The default handler is a safety net: without it, an unhandled error would have no defined consequence that a programmer could rely on.
Why the Default Handler Lives in main()
The default exception handlers are described in connection with main(), the function where a C++ program begins. The reasoning is straightforward. Handlers can be placed around any region of code, but a default that catches anything still uncaught needs to exist at the outermost level, so that no failure escapes without a trace. Placing the default around main() gives the whole program one place where leftover exceptions are reported.
Catching Versus Reporting
The sample below is an original illustration of the mechanism. A function check_index throws an exception when an index is out of range, and read_slot calls it before reading an element. In the first version, nothing catches the exception. In the second version, main places a handler around the risky call so the program can respond in its own way.
int main() {
std::vector<int> values{10, 20, 30};
read_slot(values, 7); // nothing catches the exception
std::cout << "after the call\n";
}The exception has no matching handler, so the default handler reports it on cerr and the second line is never reached.
int main() {
std::vector<int> values{10, 20, 30};
try {
read_slot(values, 7);
} catch (const std::runtime_error& e) {
std::cout << "recovered: " << e.what() << "\n";
}
std::cout << "after the call\n";
}The matching handler takes over, prints its own message, and execution continues to the line after the try block.
Tracing a Throw Through Three Functions
20
(the default handler writes an error message on cerr; the exact text depends on the implementation)check_indexis the point where the exception is thrown when the index is too largeread_slotcallscheck_indexfirst, so the throw interrupts it before v[i] is evaluated- The call
read_slot(values, 7)has no handler in any function, so the exception reaches the default handler - The line printing not reached is skipped because control left main at the uncaught throw
Following the uncaught exception step by step
In the program above, what happens when read_slot(values, 7) runs?
- 1Step 1
read_slotcallscheck_indexwith index 7. The vector has size 3, so the condition i >=v.size()is true. - 2Step 2
check_indexthrows aruntime_error. Control leavescheck_indeximmediately. - 3Step 3
The exception moves out of
read_slot. The line that would print v[i] never runs. - 4Step 4
The exception moves into main. No catch clause matches it, so it reaches the default handler.
- 5Step 5
The default handler prints an error message on cerr, and the line that prints not reached is not executed.
The valid call printed 20. The invalid call printed nothing to standard output and produced an error message on cerr.
Range Checking an Index Before Access
The word range-checked describes the behavior you would expect: the vector verifies that an index falls within the valid range of positions before accessing an element. A valid index $i$ for a vector of size $n$ satisfies $0 \leq i \leq n-1$. Any index outside that interval is one that a check would reject.
For a vector of size 5, the positions 0 through 4 are valid, and position 5 is the first position a check would reject. A check that throws on index 5 is exactly the kind of failure the default handler described earlier would report if nothing caught it.
Writing Your Own Checked Class or Using a Built-In Option
Writing exception handling around vector accesses often leads a programmer to define a checked version of vector, a wrapper that tests each index before using it. Some implementations of C++ spare you that work by supplying a range-checked version of vector already. According to the source, this is offered as a compiler option, so whether it is available depends on the implementation you are using.
| Plain vector access | Range-checked vector access |
|---|---|
| The index is used directly | The vector verifies that the index is within the valid range first |
| Protection against out-of-range access is not guaranteed in every implementation | Trades a little extra checking for protection against out-of-range access |
| Available in every implementation | Offered by some implementations, depending on the compiler option you use |
| A failure is reported only if the surrounding code happens to handle it | A failed check throws an exception, and the default handler reports it if nothing catches it |
Plain and range-checked vector access, as described in the source
Mistakes About Uncaught Exceptions
Common mistake Assuming an uncaught exception is silently ignored
Why it failsThe source states that the default handler ensures an error message appears on cerr whenever an exception is not caught.
Do this insteadExpect an error message on cerr when no handler matches, and design your reporting around that default.
Common mistake Treating the default handler as a recovery mechanism
What it looks likeRelying on the default handler to let the program continue after read_slot(values, 7) fails.
Why it failsThe default handler reports the failure. It does not resume the work that was interrupted at the throw.
Do this insteadPlace a handler where you can respond sensibly, and keep the default as the last-resort report.
Common mistake Printing the error message to standard output instead of cerr
Why it failsThe default error message is routed to cerr, the standard error-diagnostic stream, which is separate from ordinary program output.
Do this insteadWhen you write your own diagnostics for failures, send them to cerr so they can be separated from normal output.
Common mistake Believing a range-checked vector exists in every C++ implementation
Predicting Where an Exception Lands
A vector named readings has size 4. A function fetch calls check_index with the index 4, and fetch is called from main with no try block anywhere in the program. Which function in the chain receives the exception first, and where is the default error message written?
Hints
- Valid indices for size 4 run from 0 to 3, so index 4 is rejected.
- The exception leaves
check_indexfirst, then travels through fetch, then into main. - The default error message goes to cerr.
Key Takeaways
- An exception that no handler catches reaches a default exception handler, which prints an error message on cerr.
- The runtime searches for a matching handler, and an exception thrown in nested calls travels outward through them before reaching the default.
- Placing the default handler around
main()gives the program one defined place where leftover exceptions are reported. - Reporting an uncaught error is not the same as recovering from it, so catch the errors you expect.
- A range-checked vector verifies that an index is within the valid range, and some implementations offer it as a compiler option.