22

I'm having a hard time understanding this.

double compute(double x, double y) noexcept
{
   if (y == 0)
       throw std::domain_error("y is zero");
   return x / y;
}

this compiles fine in clang (I haven't checked gcc), but it seems nonsense to me. Why would a compiler allow a noexcept function to contain a throw statement?

tunnuz
  • 23,338
  • 31
  • 90
  • 128

2 Answers2

19

What will happen is std::terminate() gets triggered, since your exception specification doesn't allow for this to happen (see [except.spec/9]).

As to why it's allowed, it's simply not possible to exhaustively check if anything violates the specification. Consider something like:

double f(double );
double compute(double x, double y) noexcept
{
    return x / f(y);
} 

Can f throw? Can't say.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 7
    In your example `f` is not declared with `noexcept` specification so it may throw. The compiler can detect that. It actually does something similar with const correctness. Why can't it do that with respect to noexcept too? – Calmarius May 11 '18 at 13:02
  • 3
    @Calmarius Define similar. You want it to *not compile* if you call a non-`noexcept` function? – Barry May 11 '18 at 14:23
  • 2
    Warnings at least. Calling a throwing function in a function that's not allowed to throw is a programming error that should be caught compile time. – Calmarius May 11 '18 at 14:25
  • 2
    @Calmarius It's really not a programming error. You could be ensuring that the potentially-throwing function doesn't throw in other ways. Or the potentially-throwing function never throws anyway and was just written pre-C++11. – Barry May 11 '18 at 14:27
  • 1
    Then just propagate the noexcept to those function that will not throw. Surround the sometimes throwing functions with a try-catch(...) block to make it explicit you want to stop the exceptions from the offending function from propagating. It would be basically what Java does with its checked exceptions. – Calmarius May 11 '18 at 15:15
  • @Barry I get it that you explain how it is, but as to how it should be I have to agree with others - "noexcept" should be like const-correctness. If you mark a function "noexcept" you want it to not throw. If it can then somehow throw, you made an error. I would love to hear an official explanation why they decided to go this way. – BartekBl Nov 18 '22 at 09:17
  • @BartekBl Do you want `noexcept` to ever be usable in code, ever? Then you can't have it [color your functions](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/) that way. It would be effectively non-adoptable. Even if we had it from C++'s inception, you still couldn't call any C functions (even though C doesn't even have exceptions). If the answer is I have to write everything in `try { ... } catch (...)`, that'd be... awful (and effectively, that's what `noexcept` does anyway). – Barry Nov 18 '22 at 17:05
  • Hi @Barry. Thanks for taking the time to respond. We are developing an embedded product. It started it's life as a C project. Then we files were renamed to *.cpp and some new modules and functions are written in C++ but built with -fno-exception. As such I am 100% sure there is not a single exception in our code. I was looking forward to removing -fno-exception and using exceptions for error handling instead of error codes. The trouble is - it is an industrial embedded system. And any stray exception would kill it. I am afraid that other devs (or me when I'm tired) may let it slip. – BartekBl Nov 19 '22 at 18:33
  • @Barry I was looking forward to marking all our code as noexcept and then gradually introducing exceptions where they are appropriate. Even had I freedom to start from scratch I would still choose to mark core of our code as noexcept. It means that the core couldn't use some features that it currently reliea on like memory allocation or thread creation but that should be gotten rid of anyway. I want to use exceptionin initialisation code but not when the device is running. – BartekBl Nov 19 '22 at 18:38
  • @Barry I'm sure I missed something I wanted to say but one last thing: one of your problems is a problem because of the wrong assumption. You assume that C-declared functions don't have noexcept. The opposite should be true: all functions marked as extern "C" should be implicitly noexcept. – BartekBl Nov 19 '22 at 18:41
  • @BartekBl Marking extern "C" functions `noexcept` would be wrong - they can still potentially throw exceptions (by way of function pointers to C++ functions). – Barry Nov 21 '22 at 15:07
  • @Barry it is news to me that `extern "C"` functions can throw. Thanks for the info. But in all my C++ courses I was warned not to let any exception slip from C++ to C. Marking all `extern "C"` as implicitly `noexcept` would help with that. And C++ commitee has shown willingness to introduce breaking changes (meaning of `auto`, meaning of `throw()` etc.). So again - you say what is. I say what I think should be. – BartekBl Nov 23 '22 at 07:53
11

It is possible that a function that claims to not throw will in fact throw.
If a noexcept function does throw, terminate is called, thereby enforcing the promise not to throw at run time.

// The compiler does not check the `noexcept` specification at compile time.
void f() noexcept // Promises to not throw any exception
{
    throw runtime_error("error"); // Violates the exception specification
}

Specifying that a function won't throw promises the callers of the non-throwing function that they will never need to deal with exceptions.

Either the function won't throw or the whole program will terminate.

Andreas DM
  • 10,685
  • 6
  • 35
  • 62
  • 1
    So `noexcept` is useful for letting the compiler doing some efficiency improvements for the begging. This would be the scenario where you keep you promises. The functions without noexcept can throw exception and the others can't. If you mixed them (e.g. throw from a `noexcept` function) the compiler only crash the program for saving you ? `terminate()` is something that you should be happy for when you do so? When the program calls `terminate()` isn't already the ___runtime___? – Cătălina Sîrbu Mar 25 '20 at 19:38