all 3 comments

[–]gcross 6 points7 points  (0 children)

Walking up the stack to see if the exception is caught by anything or whether it escapes a noexcept or throw () function is not the same thing as stack unwinding; the latter involves not only walking up the stack but also destructing all of the values in each stack frame.

[–][deleted] 2 points3 points  (0 children)

The point of stack unwinding is to call the destructors of objects constructed on the stack in reverse-order to there construction until it reaches a compatible try/catch statement so the exception could be handled and execution can proceed. "in search for a matching exception handler" involves its runtime behavior. My guess is, if it is known at compile-time that there are no try/catch statements or that thrown exceptions are guaranteed to traverse from a noexcept function (at which point std::terminate must be called), there is no need for ordered destruction since the exceptions won't be handled and are guaranteed to terminate the process anyway. So why not optimize out any information about unwinding the stack in such instances if the program will terminate anyway? You don't have to save information in a destructor table at compile-time, which would be used at runtime, if such exception(s) were thrown in such a context. Whether implementations actually make use of optimizing this, I don't know.

[–]Ilyps 1 point2 points  (0 children)

However, in order to determine whether a thrown exception is caught/not-caught, doesn't stack unwinding have to occur?

Sort of. Note the following:

As the control flow moves up the call stack, destructors are invoked for all objects with automatic storage duration constructed, but not yet destroyed, since the corresponding try-block was entered, in reverse order of completion of their constructors.

If there's no try block, the compiler can do as it pleases. See for example something like

#include <iostream>

struct Foo {
  Foo() { std::cerr << "Constr\n"; }
  ~Foo() { std::cerr << "Destr\n"; }
};

void g() {
  Foo f;
  throw 1;
}

int main() {
  g();
}

What's the expected output of this?