Can a noexcept function still call a function that throws in C++17?
According to cppreference:
Note that a
noexcept
specification on a function is not a compile-time check; it is merely a method for a programmer to inform the compiler whether or not a function should throw exceptions.
So the syntax of your code is valid, but std::terminate
will be called when executed.
Unfortunately it is valid at compile time.
Although noexcept is used by the compiler to optimize the exception handling code, making the code more performant, it's a shame they didn't push it further to give noexcept a semantic meaning.
Ideally, when you mark a method as noexcept it should also mean that the method is not supposed to let any exceptions bubble up. So if you have a method marked as noexcept but it calls other methods that are not marked as noexcept, that should give you a compile error, unless there is a try/catch block surrounding anything that can throw.
Simply calling std::terminate is a very poor language design choice, because it doesn't put any responsibility at the hands of whoever writes the noexcept method. On the contrary, it harms software reuse by making it impossible even to the consumer to work the problem around.
For example, say that I am a bad library developer and I wrote the following code:
Header file Foo.h that ships with my library:
class Foo
{
public:
void DoSomething() noexcept;
};
You are happy consumer of FooLib writing a Bar application:
Bar.cpp
#include "Foo.h"
int main()
{
Foo foo;
try
{
foo.DoSomething();
}
catch (...)
{
std::cout << "Yay!" << std::endl;
}
return 0;
}
That code compiles fine and runs fine until you have Foo throwing an exception... And it doesn't make any difference if you enclose the call to foo.DoSomething() with a try/catch block. The code will simply abort.
If you don't have the code of Foo, you can't fix it. The only solution in this case is to throw away Foo library and write your own.
The contents of Foo.cpp can be something like this:
static void PotentiallyThrowException()
{
throw 0;
}
void Foo::DoSomething() noexcept
{
PotentiallyThrowException();
}
Note that it's up to the implementer of Foo::DoSomething() to wrap their own calls into try/catch. But due to the same issue, if they are calling other methods that are marked as noexcept and those developers didn't do the same, now it's Foo::DoSomething() that is hosed. And so on, and so forth.
We can safely say that from the semantic point of view noexcept is not only useless, but it's also harmful.
noexcept(true)
functions can call noexcept(false)
functions. There will be a runtime error if an exception is thrown. The canonical example of why this is allowed is:
double hypotenuse(double opposite, double adjacent) noexcept(true)
{
return std::sqrt(opposite*opposite + adjacent*adjacent);
}
std::sqrt
will throw domain_error
if its argument is negative, but clearly that will never happen here.
(In an ideal world, it would be forbidden by default with an exception_cast
to allow it where required. The result could either be UB if an exception is thrown, or std::terminate).