7 ms·
somewhat unrelated but it's worth pointing out that noexcept is more specific for move semantics. In fact most c++ developers believe that throwing an exceptio
by z_open 2y ago
somewhat unrelated but it's worth pointing out that noexcept is more specific for move semantics.
In fact most c++ developers believe that throwing an exception in a noexcept function is undefined behavior. It is not. The behavior is defined to call std::terminate. Which would lead one to ask how does it know to call that. Because noexcept functions have a hidden try catch to see if it should call it. The result is that noexcept can hurt performance, which is surprising behavior. C++ is just complicated.
- colejohnson66 2y agoI’m not well versed in C++’s exception system, but why can’t the unwind system itself call std::terminate? Why does it need to be the annotated method (that unwinding returns to)?
- epcoa 2y agoBecause the exception can’t be allowed to escape the function marked noexcept. No matter the actual implementation, the exception has to be effectively caught in that no except function.
- quotemstr 2y agoCatching is a table lookup in modern systems. I've yet to see a measurable happy path slowdown caused by adding noexcept.
- AshamedCaptain 2y agoWell TFA explains one. I also find it difficult to conceive of a case where adding noexcept would lead to slower/longer code, other than arbitrary noexcept overloads such as TFA.
- quotemstr 2y agoThe article describes the performance implications of the hash tables storing hashes. That it decides to do so based on examining noexcept() of passed in types doesn't make noexcept a pessimization itself
- AshamedCaptain 2y agoAnd, as you can see on the sibling thread where I'm being downvoted, there is an actual pessimization: since a noexcept function requires an eh_frame, it will not be able to tail-call (except for noexcept functions).
- layer8 2y agoIt doesn’t need to be, but the annotated function can still miss optimization opportunities, because it must be compiled as if it had a try-catch block if the compiler can’t prove the absence of exceptions, and this places constraints on reordering and inlining. On the other hand, the guarantee given by noexcept can enable optimizations in the caller.
- AshamedCaptain 2y agoA try { } catch block that calls terminate has no overhead. Normally the constraints on reordering are because e.g. constructor/destructor semantics and other side effects need to be accurately preserved during unwinding, but here any exception is going to result on a call to terminate, and (auto) destructors are not going to run. This was the entire point of noexcept versus the throw() specifier...
- ack_complete 2y agoThis is unfortunately not always true, even with a table-based unwinder. In order to detect the noexcept frame and call terminate(), the unwinder must be able to see the stack frame that has noexcept. This means that the compiler must suppress tail call optimization when a noexcept function tail calls a non-noexcept function.
- compiler-guy 2y agoIt also needs an entry in the exception table, and more if the cold path is moved out of line. So at the very least there are code size issues.
- klyrs 2y agoWay more history than you asked for about how we got into this situation with noexcept: Up to 2011: https://akrzemi1.wordpress.com/2011/06/10/using-noexcept/ https://akrzemi1.wordpress.com/2011/06/10/using-noexcept/ As of '17: https://devblogs.microsoft.com/oldnewthing/20180928-00/?p=99855 https://devblogs.microsoft.com/oldnewthing/20180928-00/?p=99... (I think this is the final word; I don't see any changes to exceptions in high-level release notes for c++20 and c++23 -- did I miss anything?)
- leni536 2y agoIt can, and it does on decent runtimes.
- quotemstr 2y agoYeah. Throwing from a noexcept function is often a better abort() than abort() itself because the std::terminate machinery will print information about whatever caused the termination, whereas abort will just SIGABRT.
- flamedoge 2y agowhat.. noexcept throws exception..? what kind of infinite wisdom led to this
- pavlov 2y agoNoexcept terminates if an exception is thrown within. That's a very C++ way to ensure that the exception can't propagate.
- api 2y agoThere’s a reason both Go and Rust eschew exceptions. They’re something that superficially seemed like a great idea but that in practice complicate things by creating two exit paths for every function. They also don’t play nice with any form of async or dynamic programming. C++ should never have had them, but we have sane clean C++ now. It’s called Rust.
- forrestthewoods 2y agoRust panics are basically exceptions, aren’t they? Typically they aren’t caught without terminating. But you totally can. And if you’re writing a service that runs in the background you’ll probably have to.
- wizzwizz4 2y agoCatching panics is best-effort only. In general, Rust panics can't be caught. (Even if a program is compiled with panic=unwind, this can change to abort at run-time.)
- forrestthewoods 2y agoI don’t think that’s correct. Panics can be configured to abort instead of unwind. But if panic != abort then catching should be reliable. https://doc.rust-lang.org/std/panic/fn.catch_unwind.html https://doc.rust-lang.org/std/panic/fn.catch_unwind.html
- 2y ago
- chipdart 2y ago> (...) throwing an exception in a noexcept function is undefined behavior. Small but critical correction: noexcept functions can throw exceptions. What they cannot do is allow exceptions to bubble up to function invokers. This means that it is trivial to define noexcept functions: just add a try-catch block that catches all exceptions.
- tredre3 2y agoI suppose you are technically correct that noexcept can throw to themselves. But that's just being pedantic, isn't it? From the observer/caller point of view the function won't ever throw. It will always return (or abort).
- chipdart 2y ago> I suppose you are technically correct that noexcept can throw to themselves. But that's just being pedantic, isn't it? No. There are comments in this thread from people who are surprised that you can still handle exceptions within a noexcept function. Some seem to believe noexcept is supposed to mean "don't use exception within this scope". My comment is intended to clarify that, yes, you can throw and catch any exception from within a noexcept function, because noexcept does not mean "no exceptions within this scope" and instead only means "I should not allow exceptions to bubble up, and if I happen to do then just kill the app".
- fluoridation 2y agoHmm... If you were reading the documentation for function foo() and it read "if the argument is negative, foo() throws an exception", would you understand that the function throws an exception and catches it internally before doing something else, or that it throws an exception that the caller must catch?
- deleted 2y ago[deleted]
- chipdart 2y ago> If you were reading the documentation for function foo() and it read "if the argument is negative, foo() throws an exception" (...) I think you didn't understood what I said. In C++ functions declared as noexcept are expected to not allow exceptions to bubble up. If an exception bubbles up from one of these functions, the runtime calls std::terminate. This does not mean you cannot throw and handle exceptions within such a function. You are free to handle any exception within a noexcept function. You can throw and catch as many exceptions you feel like it while executing it. You just can't let them bubble up from the scope of your function.
- yosefk 2y ago-fno-exceptions FTW. (Of course you can't always do this. Great to do this early on before it's too late and the code is full of exceptions IMO.)
- ryandrake 2y ago-fno-exceptions doesn't get rid of exceptions, it just causes your program to abort when one is thrown, which sounds--kind of worse? How do you deal with (for example) a constructor that fails? And if you're using the Standard Library, it is very difficult to avoid code that might throw an exception.
- rmholt 2y agoYou avoid any but trivial constructors an place all failable logic in a separate init()
- MauranKilom 2y agoIn other words, you introduce an invalid state to every object and make constructing objects a lot more cumbersome. The first is the exact opposite of the (imo highly desirable) "make invalid states unrepresentable" principle, and the second is also a pretty extreme cost to productivity. I wouldn't say this is never worth it, but it's a very high price to pay.
- rmholt 2y agoI know it sucks but such is the price of C++ I'm not saying it's perfect but it's better than dealing with C++ exceptions. At least with error codes you can manually do some printing to find out what went wrong. With C++ exceptions you don't even get a line number or a stack trace.
- ahoka 2y agoWell, to really implement that principle, you need a much better type system than C++ has anyways. You also don’t have to make the anemic constructor public.
- mgaunard 2y agoI believe the original proposal for noexcept was that throwing was UB, and the original author believed strongly it should have been that way. In the end what got into the standard is that it is well-defined: it calls std::terminate. If I recall that change wasn't made without drama.