Why throwing exception which is a reference calls copy constructor?
struct Error
{
Error() {}
Error(const Error&) = delete;
};
int main()
{
Error& error = *new Error;
throw error;
}
Compilation error:
error: declared here
Error(const Error&) = delete;
It does not happen when throwing pointer like:
int main()
{
Error* error = new Error;
throw error;
}
This is OK.
You cannot throw a reference. Throwing always copies the thrown expression value into a special area of storage set aside for thrown objects. Otherwise, you'd almost always be "catching" a dangling reference, as is [theoretically] the case in your code.
Your Error type cannot be copied, so the program is impossible.
However, a pointer can of course be copied, and the main problem in your final example is a memory leak. Also your program will simply terminate at the throw statement as you don't have any try/catch.
Before unwinding stack a throw operator (except throw; without an argument, used for rethrowing) creates an exception object (in a special memory area). Depending on circumstances, the object is initialized in different ways: constructor, copy constructor, move constructor (https://en.cppreference.com/w/cpp/language/copy_elision) using what was provided to the throw operator. Providing the reference is ok, but three:
if you provide a reference in an arguments list, it depends on receiving party, what is actually received, a reference or a value copy;
compiler needs to initialize an exception object, because what was provide to the throw operator will not live when the exception handling catch block is running (the stack will have been unwound then; in case of the pointer, the pointer provided, though the object it points to in your case is alive, and in the catch block you have a copy of the pointer to the same object);
it is not possible to initialize a reference in runtime;
so, in your case, the compiler expects the copy constructor in order to initialize an exception object using the reference you provided (copy constructors usually takes values to initialize the object using references to the initial one).
When you pass a reference to Error to the throw operator, the type of the exception object is Error, and we need to initialize an Error exception object in that specific memory area..
When you pass a pointer to Error to a throw operator, the type of the exception object is a pointer to Error (Error *), so the pointer is copied, not the Error object which the pointer points to. The copying pointer to error has nothing to do with calling the copy constructor of Error, so you don’t have the error in that case.
Related
To my surprise, std::runtime_error only has a const std::string& constructor, but no value constructor. Thus I am wondering if throw std::runtime_error("Temporary" + std::to_string(100)); is defined. After all we are creating an exception object that refers to a temporary object inside the scope of a function that immediately exits (as we throw out of it). Further investigation showed that while returning a constant reference to a temporary value immediately causes a segfault on my system, throwing one doesn't:
const std::string& undefined_behavior() {
return "Undefined " + std::to_string(1);
}
void test() {
throw std::runtime_error("Hello : " + std::to_string(2));
}
void test2() {
const std::string& ref = "Hello : " + std::to_string(3);
throw ref;
}
int main(int argc, char** argv) {
//std::cout << undefined_behavior() << std::endl;
try {
test();
} catch(const std::runtime_error& ex) {
std::cout << ex.what() << std::endl;
}
try {
test2();
} catch(const std::string& ref) {
std::cout << ref << std::endl;
}
}
On my system, undefined_behavior() crashes immediately but test() and test2() run fine.
While I know the call to undefined_behavior() is undefined behavior, is test() and test2() undefined? If not, do thrown values get a specfied treatment?
And if they are all undefined but happened to work on my computer by accident, what is the proper way to throw an exception with a variable message?
The const std::string& constructor doesn't cause the exception object to store a reference to the passed std::string. std::runtime_error will make an internal copy of the passed string (although the copy will be stored in an usual way, probably reference-counted in an allocation external to the exception object; this is to give better exception guarantees when the exception object is copied, which the standard requires).
A function taking a const lvalue reference doesn't usually mean that it takes ownership or stores a reference to the object.
Prior to C++11 there was no reason to not use a const reference here, since it was always more efficient than passing by-value. With C++11 move semantics that changed and it is true that usually these kind of constructors had rvalue reference variants added with C++11, so that passing a temporary std::string object to the constructor would allow more efficient move-construction instead of copy-construction.
My guess is that this simply never was done for the exception classes because the performance of their construction should not be important in comparison to the overhead of actually throwing the exception.
So in test()'s case a temporary string object is created from the + operation, which is passed to the constructor and the exception object will make a copy of it. After the initialization of the exception object the temporary string is destroyed, but that is not a problem, since the exception object stores/references a copy.
In test2() the same would apply if you had written throw std::runtime_error(ref). However, here a reference is bound immediately to the temporary object materialized from the return value of +. This causes its lifetime to be extended to that of the reference variable. Therefore again, the temporary lives long enough for the std::runtime_error to make a copy from it.
But since you write simply throw ref that is not exactly what happens. throw ref will throw a std::string, because that is the type of the ref expression. However, throw doesn't actually use the object referenced by the expression as exception object. The exception object is initialized from the operand of the throw expression. So here, the exception object will be another std::string object (in unspecified storage) which will be initialized from the temporary that ref refers to.
undefined_behavior() has undefined behavior when the return value is used, because the temporary std::string object materialized from the + return value and which the return value of undefined_behavior() references is not lifetime-extended. Although it is again immediately bound by a reference, the lifetime-extension rule specifically doesn't apply to objects bound to a reference in a return value initialization. Instead the temporary is destroyed when the function returns, causing the function to always return a dangling reference.
It is not undefined behavior.
In test1(), the constructor of std::runtime_error makes an internal copy. There is even a note on this at cppreference:
Because copying std::runtime_error is not permitted to throw
exceptions, this message is typically stored internally as a
separately-allocated reference-counted string. This is also why
there is no constructor taking std::string&&: it would have to
copy the content anyway.
In test2() there is no std::runtime_error involved. Instead, as you can read e.g. here, in the following statement
throw expr;
the exception object is copy-initialized from expr. So there is a copy made of that temporary string also in this case.
I have 3 questions :
1. It is always said to catch the exception object by reference. In the following example i see that the destructor is called before the catch block executed, that means we are referring to an object in catch which must have gone out of scope by the time we use it. Why use reference then?
class MyException{
public:
~MyException() {
cout<<"Dtor for MyException called \n";
}
};
int main()
{
try{
MyException obj1;
throw obj1;
}
catch(MyException& obj)
{
cout<<"Catched unhandled exception";
}
}
2. Why is the destructor called twice here? Once before entering the catch block and second time after execution of catch completes.
3. The example in Lifetime of a thrown object caught by reference shows that the destructor is called just once i.e. after the catch block exits ONLY WHEN a copy constructor is defined in the class.
a. What is the role of copy constructor here when we are catching it by refrence?
b. Even after defining a copy constructor it never gets called anyways. So what impact does it make?
c. I tried to define a copy constructor in my example too as below but still i see destructor getting called twice.Why?:
MyException(const MyException& obj)
{
}
Can somebody answer all the 5 questions(3rd question has 3 parts).
You're throwing a copy of the exception you created as a local temporary. So there's two: the first is destroyed when you throw, the second (the copy) after the catch block is done.
Try this:
try { throw MyException{}; }
catch (MyException const& obj) { }
Edit: If your copy constructor is really not being called in your code then my guess is that the compiler has recognized that the exception class is empty? Compiler is free to perform any optimizations it chooses so long as the behavior of the code is as-if it hadn't. Exception to this is copy-ellision but if that was going on you'd not get the double destructor call.
throw ALWAYS makes a copy of an object you're throwing. You can't throw an object which is not copyable. If fact, throw can even fail if it can't allocate enough memory for a copy. This should explain why you see a destructor being called inside a try-block. It's a destructor of your local obj1. It goes out of scope as soon as your throw a copy of it.As for catching by reference, it is used to avoid unnecessary copying and, more importantly, potential slicing.
Once again, it was a destructor of your local obj1. It went after out scope when you threw a copy of it.
a) If you catch by reference, no copying is performed. Copying (with potential slicing) is performed ONLY if you catch by value.
b) You're wrong, throw calls a copy constructor to, well, make a copy of your object and throw it. The only case when a copy constructor won't be called is when you throw a temporary object and compiler elides a copy.
c) Refer to my first and second answer.
They recommend you take your catch block by reference to avoid slicing (since inheriting exceptions is standard) and to avoid needlessly copying, since throw copies unconditionally.
You cannot receive obj1 by reference as throw obj1 causes it to be destroyed (since it is no longer in scope). Due to this throw always copies to temporary memory location that you don't control.
obj1 and the temporary I mentioned before. obj is a reference to that temporary, the referenced temporary will be destroyed after the end of the catch block and before the next operation (similar to if it were actually local to the catch block).
A single destructor only happens when the copy is elided and the standard doesn't guarantee when that will happen, as copy elision is always optional. Defining a copy constructor makes no difference as the compiler defines it for you if you don't define it (and it can be created). Deleting the copy constructor would just result in throw being illegal.
a. As above, copy constructor to copy into the temporary. Reference to reference said temporary.
b. Did you actually delete the copy constructor? MyException(const MyException&) = delete; is how you delete a copy constructor, failing to define it just causes the compiler to make one for you. And to repeat, deleting it causes throw to be illegal.
c. Because the copy isn't elided and two objects exist, a temporary and your object.
class Error1
{
public:
int errorcode;
Error1(int x):errorcode(x){ cout<<"CTOR Error1"<<endl; }
//Error1(Error1& obj ){
// errorcode = obj.errorcode;
// cout<<"CopyCTOR Error1"<<endl;
//}
~Error1(){cout<<"DTOR Error1"<<endl; }
};
void fun()
{
cout<<"Inside fun"<<endl;
throw(Error1(5));
}
int main()
{
try{
fun();
}
catch(Error1& eobj)
{
cout<<"Error1 type occured with code:"<<eobj.errorcode<<endl;
}
cin.get();
}
OUTPUT:
Inside fun
CTOR Error1
DTOR Error1
Error1 type occured with code:5
DTOR Error1
This output indicates that a Error1 object is copy constructed for the catch handler. Since copy constructor is not defined for Error1 object default copy constructor is used.
When i uncomment the commented section for defining a copy constructor i get the the following output.
Inside fun
CTOR Error1
Error1 type occured with code:5
DTOR Error1
Why is it that only one DTOR is getting called? Even if exception is caught by reference i believe a temporary is still created.
What compiler are you using?
When you introduce (i.e. uncomment) your version of copy constructor with Error1& obj argument, the code is supposed to become invalid. throw is supposed to be able to create a copy of its argument, while your version of copy constructor disables copying of temporaries. The code is ill-formed. If your compiler accepts it, its is probably because it illegally allows binding non-const references to temporaries (I suspect it is MSVC++ compiler with extensions enabled).
The original experiment works as it is supposed/allowed to. The argument of throw is copied to an internal temporary which is later used to initialize catch parameters. Although the compilers are allowed to use your original temporary directly, extending its lifetime accordingly.
There may be other errors, but what I see right now is that throw(Error1(5)); creates a temporary (or rvalue) of type Error1. You want an lvalue, which means that you should either do throw(*new Error1(5)); (which I believe will create a memory leak, but I may be wrong), or you could create a global Error1 object and just throw that.
PS: I would be interested to know if throw(*new Error1(5)); does create a memory leak, if anyone would like to comment. Does catch destroy the object it catches? If so I think you should be fine with just creating new Error1s whenever you need them.
I have the below exception class.
class ExceptionTest : std::exception
{
public:
ExceptionTest(int value):
m_value(value)
{
}
~ExceptionTest()
{
}
private:
ExceptionTest(const ExceptionTest& test)
{
}
int m_value;
};
I then use it in this way -
int checkexception()
{
throw ExceptionTest(2);
}
int main()
{
try
{
checkexception();
}
catch (ExceptionTest& exception)
{
cout<<"haha";
}
return 1;
}
This works perfectly fine even though the copy constructor is private.
If you catch the exception by value it fails -
int main()
{
try
{
checkexception();
}
catch (ExceptionTest exception) --> this fails
{
cout<<"haha";
}
return 1;
}
the error i get is
error C2316: 'ExceptionTest' : cannot be caught as the destructor and/or copy
constructor are inaccessible
I get a linker error if I do not define the copy constructor in the class
class ExceptionTest : std::exception
{
public:
ExceptionTest(int value):
m_value(value)
{
}
~ExceptionTest()
{
}
private:
ExceptionTest(const ExceptionTest& test);
int m_value;
};
LINK : C:\Users\sumitha\Documents\Visual Studio 2010\Projects\test\Debug\test.exe not found or not built by the last incremental link; performing full link
1>main.obj : error LNK2001: unresolved external symbol "private: __thiscall ExceptionTest::ExceptionTest(class ExceptionTest const &)" (??0ExceptionTest##AAE#ABV0##Z)
1>C:\Users\sumitha\Documents\Visual Studio 2010\Projects\test\Debug\test.exe : fatal error LNK1120: 1 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
If the above is true, we can always make the copy constructor of the exception class private, so that the caller would be forced to catch the exception by reference. I think this is happening because of "return value optimization"
This appears to be an error in VS2010's C++ implementation.
When you throw an expression a temporary exception object is created by copying (or moving) the operand of the throw expression. If the expression has class type this involves the copy (or move) constructor and the constructor must be accessible at the point of the throw. If the copy constructor of an exception object is private then that object could only be thrown from a member function or a friend function.
This concern is completely independent of whether the exception object is subsequently caught by value or by reference later in the program.
In constructing the temporary exception object, the actual copy may be elided but C++ requires that the constructor that would have been used still be accessible.
ISO/IEC 14882:2003 15.1 [except.throw] / 5:
If the use of the temporary object can be eliminated without changing the meaning of the program except for the execution of constructors and destructors associated with the use of the temporary object (12.2), then the exception in the handler can be initialized directly with the argument of the throw expression. When the thrown object is a class object, and the copy constructor used to initialize the temporary copy is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated).
Similarly, if the destructor for that object is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated).
This requirement has not been removed in C++0x although the throw expression may now be moved instead of copied where appropriate.
Draft n3291 15.1 [except.throw] / 5:
When the thrown object is a class object, the copy/move constructor and the destructor shall be accessible, even if the copy/move operation is elided (12.8).
Like Marino said, if you catch by reference there is no copy construction involved.
So, the compiler doesn't look for it, and never fails on a private constructor :)
As a sidenote, I recall that specifically for exceptions there is an 'exception' (no pun intended) in the specs regarding the pass-by-value semantics. I don't know the specifics anymore, but it has to do with
passing local exceptions from an auto variable (stackallocated) which is naturally a bit tricky while unwinding the stack
perhaps active specifically in the case of a re-throw of an exception that was caught by value
.
....
catch (std::runtime_error e)
{
// ....
throw;
}
When you catch the exception using reference (& symbol) you will get the same exception object that was generated i.e the variable in the catch block will be pointing to same exception object in memory that was thrown and hence no need to have a copy cons. Where as if you use the other catch block which catches the exception by value then code needs to be generated by the compiler to create a copy of the exception object that is being caught and assign this new copy to the variable in the catch block, hence you need to have a public copy cons.
Making the copy constructor (and assignment operator) private is a very widely used technique in C++ to prevent copying. Arguably, the vast majority of C++ classes (but not exceptions, which must have an accessible copy constructor) should have copying disabled - I know that almost all of the classes in my own code do this.
I've just fixed a very subtle bug in our code, caused by slicing of an exception, and I now want to make sure I understand exactly what was happening.
Here's our base exception class, a derived class, and relevant functions:
class Exception
{
public:
// construction
Exception(int code, const char* format="", ...);
virtual ~Exception(void);
<snip - get/set routines and print function>
protected:
private:
int mCode; // thrower sets this
char mMessage[Exception::MessageLen]; // thrower says this FIXME: use String
};
class Derived : public Exception {
public:
Derived (const char* throwerSays) : Exception(1, throwerSays) {};
};
void innercall {
<do stuff>
throw Derived("Bad things happened!");
}
void outercall {
try {
innercall();
}
catch(Exception& e)
{
printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
throw e;
}
}
The bug was of course that outercall ends up throwing an Exception, instead of a Derived. My bug resulted from higher in the call stack attempts to catch the Derived failing.
Now, I just want to make sure I understand - I believe that at the 'throw e' line, a new Exception object is being created, using a default copy constructor. Is that what's really going on?
If so, am I allowed to lock out copy constructors for objects that will be thrown? I'd really prefer this not happen again, and our code has no reason to copy Exception objects (that I know of).
Please, no comments on the fact that we have our own exception hierarchy. That's a bit of old design that I'm working to correct (I'm making good progress. I've gotten rid of the home-grown string class, and many of the home-grown containers.)
UPDATE: To be clear, I had fixed the bug (by changing 'throw e' to 'throw') before I ever asked the question. I was just looking for confirmation of what was going on.
When you throw an object, you're actually throwing a copy of the object, not the original. Think about it - the original object is on the stack, but the stack is being unwound and invalidated.
I believe this is part of the standard, but I don't have a copy to reference.
The type of exception being thrown in the catch block is the base type of the catch, not the type of the object that was thrown. The way around this problem is to throw; rather than throw e; which will throw the original caught exception.
A quick google suggests that yes, you're throwing the copy constructor is required and must be public. (Which makes sense, as you're initializing a copy of e and throwing that.)
Anyway, just use throw without specifying the exception object, to rethrow what was caught in the catch. Shouldn't that solve the problem?
catch(Exception& e)
{
printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
throw;
}
Yes.
throw e;
throws an exception of the static type of e, regardless of whatever e actually is. In this case, the Derived exception is copied to an Exception using a copy constructor.
In this case you can just
throw;
to get the Derived exception bubble up correctly.
If you're interesting in polymorphical throwing in some other cases, refer to the always so useful C++ FAQ Lite.
C++ never ceases to amaze me. I would have lost a lot of money had this been a bet on what was the behaviour!
The exception object is first copied to a temporary and you should have used throw. To quote the standard 15.1/3:
A throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from "array of T" or "function returning T" to "pointer to T" or "pointer to function returning T",respectively.
I think this gives rise to a very useful coding standard rule:
Base classes of an exception hierarchy should have a pure virtual destructor.
or
The copy constructor for a base class in an exception hierarchy shall be protected.
Either achieves the goal that the compiler will warn when you try to "throw e" since in the first case you cannot create an instance of an abstract class and the second because you cannot call the copy constructor.