Except for destructors, swap functions, move constructors and move assignment operators, the standard marks a function noexcept
only if it has a wide contract, i.e., it has no preconditions. This overload requires the argument to be a null-terminated string, so the standard does not mark it as noexcept
.
The rational is specified in N3248:
Functions marked noexcept
are difficult to test
When a function is marked with noexcept
it becomes impossible to flag test failures,
notably in test drivers, by throwing an exception. A common example would be code
that validates preconditions on entry to a function:
T& std::vector<T>::front() noexcept {
assert(!this->empty());
return *this->data();
}
When validating such defensive checks from a test driver, a reasonable approach is to
register an assert-handler that throws a well-defined precondition-violated exception,
which the test driver catches to ensure that the appropriate assert
s are indeed in
place.
...
Now we might argue that calling the function out-of-contract, when the vector
is
empty, is undefined behavior so we should not expect any guarantees. The problem is that undefined behavior is being specified by the library; to the compiler, this code is
perfectly well defined and, if assert
throws an exception, the program must terminate
in a well-specified manner, thwarting the test driver.
Note that the issue here is not that we are using assertions to find bugs in our own
library implementations, but rather in user code that incorrectly calls into our library.
If we remove the ability to test these defensive assertions, we may get them wrong,
and thus put our users at risk for committing far more serious errors than propagating
an unexpected exception.
By the way, due to [res.on.exception.handling]/5:
An implementation may strengthen the exception specification for a non-virtual function by adding a non-throwing exception specification.
... libstdc++
and libc++
are free to mark this overload noexcept
.