Marking a function `noexcept` that could cause an exception constructing the returned object

C++17 had a wording change that added sequencing around the return statement. The following paragraph was added.

[stmt.return]

3 The copy-initialization of the result of the call is sequenced before the destruction of temporaries at the end of the full-expression established by the operand of the return statement, which, in turn, is sequenced before the destruction of local variables ([stmt.jump]) of the block enclosing the return statement.

The result object is initialized before that local variables in scope are destroyed. This means the throw is in the scope of the function. So, any exception thrown at this point is not on the caller side.

As such, marking the function as noexcept is gonna make the program terminate.

RVO doesn't change that. It only affects at what storage the result object is initialized in, but the initialization itself is still part of the function's execution.


In this case, would the exception be propagated to the caller (i.e. it's considered to happen in main) and thus would be handled in the catch(...) handler?

I disagree. The copy must be done in the function scope as part of the return statement expression. Because local destructors are called only after return and they are definetely in the function scope.

Yes, C++17 made some guarantees about RVO, in particular this example is now guaranteed elision:

struct Foo{};

Foo bar(){
    Foo local;
    return Foo{};
    // Foo:~Foo(local);
}

Foo var = bar();

Still, if Foo:Foo() throws, the function is not noexcept. All RVO says that there is no move nor copy into var variable and Foo{} expression constructs the object at the location var. Yet it does not change when the object is constructed - in the function scope, before the destructors are even called.

Furthermore, mandatory RVO does not apply here since v is not a prvalue but l-value.