20 comments

[ 2.6 ms ] story [ 47.0 ms ] thread
> The noexcept specifier protects C++ program from memory leaks and potentially other undesired behaviors brought by throwing exceptions.

While I agree with the presented observation, just letting the exception bubble up to the top would have done the same thing. The memory leak presented here is just the result of not using RAII to clean up the resource.

Throwing an exception through a `noexcept` function terminates (or rather calls std::terminate) because you are doing something you explicitly said you wouldn't do, pass an exception through this function.

> For key functions we hardly know how to handle its exceptions, it might be a good idea to add the noexcept specifier.

On the contrary, `noexcept`s usecase is in places where we want to be sure a function doesn't just throw an exception, as this may thwart some guarantees we want to make to the caller. Sprinkling your functions with `noexcept` just to abort (which is already happening if the exception is unhandled) is bad code. Plus "we hardly know how to handle its exceptions" is the worst cop-out I've heard for dealing with exceptions - C++ exceptions are an essential part of the language and many libraries, so dealing with them should be a prime concern of the programmer.

I agree with your pointa. A note though, I also used to think exceptions are "essential", but after working with code that forbids exceptions and uses explicit error status passing, I find exceptions very awkward to work with. I find them a very lazy way of doing error propagation, which in turn encourages lazy error handling.
C++ relies heavily on error handling using out-of-band exceptions. f(g(h())) needs h and g to return meaningful values rather than errors, constructors can't return errors at all, and member initializer lists can't check for returned errors.

You could make a dialect in which every method returns an error and every object has an init(...) method instead of a ctor, but it would be closer to imperative VB than idiomatic and readable C++.

also note that Java has checked exceptions, and a lot of programmers still opt to deal with errors in a lazy way.

Same for error codes, go has explicit error, and several programmers still opt to deal with errors in a lazy way.

I think the syntax and semantical contructs a language offers doesnt matter much when a lot of programmers dont have time and/or discipline and/or priority to deal with error cases.

Error handling is hard. Also, since programmers in general think less about errors, library often dont expose errors in helpful ways and/or well-defined ways.

> Also, since programmers in general think less about errors

That's one reason I like Go's approach, even if the boilerplate is ugly. When writing Go code, I'm pushed to think about the `err` returned value. When reading Go code, I know directly how `err` is handled, without any surprise, and if it isn't then that should be for a reason, so something to dig out. While it's quite annoying at first, after some time it becomes quite easy to either mentally filter out the error handling boilerplate (thus focus on the actual logic), or focus on it.

Of course, YMMV.

There's some boilerplate (a bit less compared to Go) required to deal with errors in Rust too, but additionally, the compiler can enforce you can't get at the return value without dealing with the error variant. I just checked that there are also static checkers for Go that'll tell you if you forgot to check an error.
Both standard library and other libs make use of them, so you often can't just ignore exceptions.

Also agree on your point - I'm not saying I like exceptions, I like explicitly passing type-encapsulated results/errors more for clarity. Exceptions happen in a "second invisible lane" right next to the code you are writing, and for example aren't immediately visible when reviewing code.

> Exceptions happen in a "second invisible lane" right next to the code you are writing, and for example aren't immediately visible when reviewing code.

hm, that's interesting - when doing C++ I always see the "bubbling-up" lane

> int* array = new int[n];

that's why you do `auto array = std::make_unique<int[]>(n);` (or `auto array = std::make_unique_default_init<int[]>(n);` if you don't believe in your compiler's ability to optimize redundant writes)

Also, this "memory leak" is only an issue on pre-1990 operating systems as "current" operating systems have no trouble reclaiming unfreed memory - you loose exactly the same amount of memory in both cases : zero.

So no, don't litter noexcept everywhere, especially for that, this article just does not make sense.

> Also, this "memory leak" is only an issue on pre-1990 operating systems as "current" operating systems have no trouble reclaiming unfreed memory

Not just that, the OS is faster to reclaim the unfreed memory than if the program was cleaning it up.

It's only true if the program is about to exit, for long running programs like daemons, ``memory leak`` is still a thing.
Also, make_unique<int[]> would not be UB: The post matches new[] with delete and does not even care about valgrind mentioning this.
And critical embedded OSes running on IoT devices, but who cares about that. /s
> And critical embedded OSes running on IoT devices, but who cares about that.

are there current non-homegrown RTOSes that do not reclaim memory ? I know that QNX does.

Probably not, but not having to depend on such factors is good engineering and security best practices.
A perfect example of the small-scale-example syndrome. As somebody else noted here, throwing an exception in a noexcept context calls std::terminate. Figuring out why terminate was triggered in a large, multithread program sounds like a funny task until you have to do it by yourself.

The only place for noexcept are getters that return a copy of a POD value or a (const-)reference. Placing noexcept anywhere else is a call for problems.

Can the terminate handler examine the stack and/or the thrown exception in order to provide useful clues to the developer, or is it too late by then?
In GCC runtime the terminate handler is called after stack unwinding.
Just use a finally block to free the array even in case of exceptions. Or does C++ not have finally blocks?
It does not have finally blocks. However, correct use of RAII should be used to automatically free resources as they go out of scope.