nullplan wrote:Well, it is well known (even for me, a C guy) that C++ destructors should really not throw because of that problem. There is also the philosophical problem "what does it mean if destruction fails?", that most people probably don't want to tackle (it is similar to "what happens if close() fails? Is the FD closed or not?")
Yep, I agree about the philosophical problem. But no matter what, dtors should not throw. Ultimately, if dtors have critical code that should release critical resources and that operation might fail, the whole code has to be re-designed to not use dtors, but explicit clean-up methods or, dtors can enqueue the real clean-up code for async processing in another thread. It should be possible to transfer the ownership of the inner object (e.g. moving an unique_ptr etc.)
nullplan wrote:But I believe you miss the point of "noexcept": It is a promise from the programmer to the compiler that the code will not throw. If it does anyway, that is undefined behavior (though the C++ people like to bandy the term "ill-formed program" about). The core of C has always been to trust the programmer, and the C++ people have kept that alive. "noexcept" is not a magical exception repellent. It is similar to "restrict" in C99: You have to ensure the promise is kept, and if not, things break. You can even call functions that throw in case of invalid arguments if you ensure your arguments are always valid.
I'm not missing the point. I don't like that design decision, as I don't like almost anything around UB, as you probably already know from my infamous thread
I don't like the idea of making a promise that could or could not be honored, given that, theoretically, we know this at
compile time.
nullplan wrote:Having a tool that detects such unsafe calls might be good idea, but would probably be very noisy for the above reason. But that tool does not belong in the compiler for the same reason.
I disagree. If you always honor the promise to not throw in noexcept functions, you'll get 0 warnings/errors. That's the whole point: enforce the rules in 100% of the cases. You can still have "trusted" functions that can call throwing functions and be called by noexcept, because they promise to catch all the exceptions. And here we open another can of worms. How can those trusted functions be sure they'll catch all the exceptions, unless they use catch(...) ? Well, that's harder. It would seem possible to add to the ABI the list of possible exceptions that a function (directly or not) might throw.. but in reality that would mean having some functions which can throw 100s of different exceptions and that's non-sense. It might be OK to check that with static analysis, but not in the ABI. Therefore, we could add just 2 bits of info in ABI for 3 cases: { except, noexcept, trusted }. It has already been done for const (in order to have deep const-correctness), I don't know why nobody wanted to do the same for noexcept.
nullplan wrote:I was about to ask how other languages solve the same problem, but then remembered that most other OO languages use garbage collection and don't have the problem of throwing destructors, because they have no destructors.
Yep, exactly. It's really an unsolved problem.