Suppose I have a function returning by value
S f();
and I call it like that:
auto s = f();
If copy-constructor of S
throws, and return some_s;
inside f
is surrounded by try-catch block,
is it guaranteed that the exception will be captured
inside f
rather than being thrown on the call site?
I realize that auto s = f();
is subject to mandatory copy elision as of C++17, so there is (at most) one call to copy-constructor of S
. But is it guaranteed to happen inside f
from the point of view of exception handling?
Tests in gcc, clang, mingw and msvc indicate that this is indeed so (the code is below). What bothers me is that
In any case, some quotes from the standard will be appreciated ;)
Here is the code I used for testing
#include<iostream>
struct S{
S(const S &){
throw "Oops";
}
S(int) noexcept {}
};
S f(){
static/*to avoid RVO*/ S stat(7);
try{
return stat;
}catch(const char *what){
std::cerr << "still in function after failed-return" << std::endl;
}
return S(7);// mandatory copy-elision (c++17);
}
int main()
try{
auto s = f();// mandatory copy-elision (c++17);
}catch(...){
std::cerr << "Exception from main..." << std::endl;
}
Since C++17, there isn't a copy after f
returns. The return value of f
is an unmaterialised temporary value, which is materialised in the initialisation of s
, it isn't an object, and doesn't need to be copied to initialise something.
It is not "mandatory copy elision". The lifetime rules were changed such that there isn't an object (yet).
it seems odd that return does not actually transfer control out of a function
You can have arbitrarily complex sub-expressions in a return
statement, which can throw. If they do, you are allowed to catch them within the function. It would be highly inconvenient if you couldn't.