Reputation: 198
I want to know if writing exceptions inbox and out box is changing the behavior or not of a particular program, for example throw MyException(); and throw (MyException());
My Code:
#include <iostream>
#include <exception>
using namespace std;
class MyException: public exception {
public:
virtual const char* what() const throw()
{
return "Something bad happened";
}
};
class Test
{
public:
void goWrong()
{
throw (MyException());
}
};
int main()
{
Test test;
try
{
test.goWrong();
}
catch (MyException &err)
{
cout << "The Exception is Executed: " << err.what() << '\n';
}
cout << "Still Running" << '\n';
return 0;
}
Upvotes: 7
Views: 142
Reputation: 17454
The exception object is copy-initialised (except.throw/3
), so it doesn't really matter which one you use; the result is the same.
Copy-initialising would ignore a reference qualifier even if you got one out of this.
We can prove this with some trace output:
#include <cstdio>
using std::printf;
struct T
{
T() { printf("T()\n"); }
~T() { printf("~T()\n"); }
T(const T&) { printf("T(const T&)\n"); }
T(T&&) { printf("T(T&&)\n"); }
T& operator=(const T&) { printf("T& operator=(const T&)\n"); return *this; }
T& operator=(const T&&) { printf("T& operator=(T&&)\n"); return *this; }
};
int main()
{
try
{
throw T();
}
catch (const T&) {}
}
Even if you switch from throw T()
to throw (T())
the semantics (and output) are exactly the same:
T()
T(T&&)
~T()
~T()
That is, a temporary T()
is constructed, then moved into the real exception object (which exists in some magical "safe space"), and ultimately both are destructed.
Note that, to see this proof, you do have to go back to C++14 (as C++17 doesn't materialise that temporary until the real exception object is needed, per so-called "mandatory elision") and turn off pre-C++17 optional elision (e.g. -fno-elide-constructors
in GCC).
If, as some others have claimed, there could be such a thing as "throwing a reference" (which became dangling), you'd only ever see one construction of T
. In truth, there is no such thing as expressions of reference type, despite the best efforts of decltype
and auto
to pretend to you that there are.
In both cases, the expression we give to throw
is an rvalue MyException
.
The reason we have to be careful with return
when using a deduced return type (via decltype(auto)
), is that deduction considers value category. If you have a local variable int x
, then in return x
the expression x
is xvalue int
, so your deduced type will be int
and everything is fine. If you write return (x)
instead then the expression is lvalue int
, which results in a deduced type of int&
and suddenly you have problems. Notice that neither expression has reference type (a thing which effectively does not exist). But exactly none of this is relevant in your throw
scenario anyway, not least of all because your argument is a temporary in the first place.
Upvotes: 6
Reputation: 2598
I believe it would work like a return statement in C++14 and later, in that parentheses indicate the type to be thrown to be a reference, not the exception instance itself. The reference would be stored and copied and treated like an exception, but the temporary object the reference points to would probably be destroyed in stack unwinding, which may cause problems. It may not cause immediate issues if your exception object is of POD type, since destruction for such an object created on the stack is trivial, but it's still undefined behavior.
Upvotes: -5