Why Vector Access Operators Are Not Specified as Noexcept

Why vector access operators are not specified as noexcept?

The standard's policy on noexcept is to only mark functions that cannot or must not fail, but not those that simply are specified not to throw exceptions. In other words, all functions that have a limited domain (pass the wrong arguments and you get undefined behavior) are not noexcept, even when they are not specified to throw.

Functions that get marked are things like swap (must not fail, because exception safety often relies on that) and numeric_limits::min (cannot fail, returns a constant of a primitive type).

The reason is that implementors might want to provide special debug versions of their libraries that throw on various undefined behavior situations, so that test frameworks can easily detect the error. For example, if you use an out-of-bound index with vector::operator[], or call front or back on an empty vector. Some implementations want to throw an exception there (which they are allowed to: since it's undefined behavior, they can do anything), but a standard-mandated noexcept on those functions makes this impossible.

Is array::operator[] actually noexcept?

The reason that operator[] is not marked as noexcept is that it has a "narrow contract", i.e, the index value is required to be in the range 0 ... N-1. If the value passed is not in that range, the behavior is not defined, and (who knows?) the function might throw.

The standard is pretty consistent about not marking things with "narrow contracts" as noexcept. This is informally known as "the Lakos rule".

Note that library IMPLEMENTERS have the freedom to add noexcept where the standard does not specify it, if they choose. I'll think about adding that to libc++.

[Later: It turns out that libc++ does this already for string and string_view, but not vector, array or deque]

[Sill later: libc++ now marks operator[] as noexcept for vector/array and deque.]

Why are std::array::front and std::array::back not noexcept?

From cppreference

There is a special case for a zero-length array (N == 0). In that case, array.begin() == array.end(), which is some unique value. The effect of calling front() or back() on a zero-sized array is undefined.

So since we can have a 0 sized array front() and back() could cause an exception

To quote Sebastian Redl on why the standard doesn't mandate operator[], front and back be marked noexcept

The standard's policy on noexcept is to only mark functions that cannot or must not fail, but not those that simply are specified not to throw exceptions. In other words, all functions that have a limited domain (pass the wrong arguments and you get undefined behavior) are not noexcept, even when they are not specified to throw.

Why some C++ standard functions are missing literal exception specification or not marked as conditionally noexcept?

Here is a paper (quoted by some other standard library proposals) for reasons to not specify noexcept on some standard library functions: N3248: noexcept Prevents Library Validation

If a function that the standard says does not throw any exceptions does throw an exception, you have entered undefined behaviour territory. There is a bug in your code. Catching and swallowing it is certainly not the right thing to do.

For example in this code:

std::set<int> s;
try
{
s.erase(s.cbegin());
}
catch (...) {}

may make s.erase throw a C++ exception in debug mode since the preconditions are not met (s.cbegin() is not dereferenceable), making this run and seem to work unnoticed, but suddenly some other behaviour happens (like a crash or an infinite loop) in release mode.

If the standard mandated that this function was noexcept, the function could not throw an exception even in debug mode.

However, standard libraries are allowed to add noexcept specifiers even if not explicitly stated in the standard, which many do. This gives the freedom for a standard library implementor to do what is appropriate (e.g., noexcept(true) in <release_set>, but noexcept(false) in <debug_set>)

And, of course, if you know the preconditions are met (like if (!s.empty()) s.erase(s.cbegin());), you know an exception can't be thrown and don't need to write any exception handling.

See also: Why vector access operators are not specified as noexcept?

Should a function which can fail only due to integer overflow be noexcept?

As far as I know, noexcept should be interpreted as "nofail"

That's not correct. noexcept literally means "I promise this function will never throw an exception." There are myriad other types of failures, such as segmentation faults, illegal instructions, the calling of pure virtual functions, integer divide by zero, not to mention "Bob in accounting told me all our customer numbers consist only of digits, but I just found out our very first customer's ID was actually Q001 and it doesn't parse." None of those will necessarily result in an exception, and neither will signed integer overflow, so functions failing in those ways can still be noexcept even though they can fail--they just can't throw C++ exceptions.

You may then wonder, "What happens if an exception is thrown by a noexcept function?" In that case, std::terminate() will be called, and your program will end.

C++ exception, undefined behavior and noexcept

Should I define it as noexcept?

You certainly can, if you want. It is true in that the function won't throw an exception. It still can fail from division by zero, however.

It depends on what you think noexcept should mean. The standard library seems to treat it as a keyword meaning the function can't possibly fail, not just that it doesn't throw. If you follow their convention, you shouldn't make it noexcept. However, if you want noexcept to mean only that it doesn't throw, then you should.

Either way, you should always use the same convention. Outside of constructors and assignment operators, there isn't a large benefit either way.

Why std::map find() is not declared as noexcept?

find() are based on the Compare() method of the map, that could throw an exception (imagine the case of a complex key that could be incorrect). So, we can not be sure that find() won't raise an exception.



Related Topics



Leave a reply



Submit