I have a class CustomException, that implements std::exception, in which I explicitely deleted the copy and move constructors. When I throw an exception of that class, there are compiling errors for calling the deleted constructors.
Are CustomException instances being created somewhere? What objects are created when the exception is thrown?
When you throw, an exception object is constructed that has the same type as the operand of throw with top-level cv-qualifiers removed (if you throw an array or function, they also decay to their corresponding pointers).
So what you did is a no-go, I'm afraid.
C++ standard chapter [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).
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. So the information is available from the exception object, which is alive until completing handling the exception, though what was provided to the throw operator is destroyed when the stack is unwound).
In your case the compiler needs the functions you deleted to either initialize the exception object when throwing the exception or to initialize the catch-clause argument, or both - since it is how the compiler does it by design (using the functions).
Related
void MyClass::method()
{
SomeObject a;
methodThatCanThrowException();
}
I wish to catch exceptions higher up the call-stack, not in method() - but in this example will the SomeObject destructor be called or not?
Yes. That's why RAII works. It's why exceptions work. They wouldn't otherwise.
Full standard quote:
15.2 Constructors and destructors [except.ctor]
1. As control passes from a throw-expression to a handler, destructors are invoked for all automatic objects constructed since the try block was entered. The automatic objects are destroyed in the reverse order of the completion of their construction.
2. An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object’s destructor will be invoked. If the object was allocated in a new-expression, the matching deallocation function (3.7.4.2, 5.3.4, 12.5), if any, is called to free the storage occupied by the object.
3. The process of calling destructors for automatic objects constructed on the path from a try block to a throw-expression is called “stack unwinding.” If a destructor called during stack unwinding exits with an exception, std::terminate is called (15.5.1). [ Note: So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note ]
Yes. It's sometimes called stack unwinding
Catching exceptions higher up the stack is often a good idea. I've seen lots of code that catches exception when it can't do much about it.
Just make sure SomeObject's destructor doesn't throw an exception.
I am learning about the RAII idiom in C++, and how to use smart pointers.
In my reading, I have come across two things that, to me, seem to contradict each other.
Quoted from http://www.hackcraft.net/raii/:
...if a member object with RAII semantics has been created and an exception happens before the constructor has completed then its destructor will be called as part of the stack unwinding. Hence an object which controls multiple resources can guarnatee their cleanup even if it isn’t fully constructed by using member RAII objects.
But quoted from http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.10:
If a constructor throws an exception, the object's destructor is not run. If your object has already done something that needs to be undone (such as allocating some memory, opening a file, or locking a semaphore), this "stuff that needs to be undone" must be remembered by a data member inside the object.
And then the second linked source recommends using smart pointers to deal with the issue of things that were already allocated in the constructor.
So what actually happens in these scenarios?
You're misunderstanding the first quote. That's not hard, since it's confusing.
if a member object with RAII semantics has been created and an exception happens before the constructor has completed then its destructor will be called as part of the stack unwinding.
That's what it says. Here's what it meant:
if a member object with RAII semantics has been created and an exception happens in the outer object before the outer object's constructor has completed then the member object's destructor will be called as part of the stack unwinding.
See the difference? The idea is that the member object completed its constructor, but the owning type didn't. It threw somewhere in its constructor (or a constructor of another member that is initialized after that one). This will cause the destructor of all of its members to be called (all of the ones that completed construction, that is), but not its own destructor.
Here's an example:
class SomeType
{
InnerType val;
public:
SomeType() : val(...)
{
throw Exception;
}
};
When you create a SomeType instance, it will call InnerType::InnerType. As long as that doesn't throw, it will then enter SomeType's constructor. When that throws, it will cause val to be destroyed, thus calling InnerType::~InnerType.
There's no contradiction here; there's just some confusing terminology being used in different contexts.
If an object's constructor throws an exception, then the following occurs (assuming the exception is caught):
All local variables in the constructor have their destructors invoked, releasing all resources they've acquired (if any).
All of the direct subobjects of the object whose constructor threw an exception will have their destructors invoked, releasing resources they've acquired (if any).
All base classes of the object whose constructor threw will have their destructors invoked (since they were fully constructed before the derived class constructor ran)
Further cleanup from the caller etc. will take place.
As a result, any resources that are managed by smart pointers or other RAII objects that are data members of the object being destructed will indeed be cleaned up, but specialized code to do cleanup in the destructor of the object won't fire.
Hope this helps!
These two statements don't contradict each other, but the first one has some unfortunate language. When the construction of some object throws, it's deconstructor won't be called, but all objects owned by that object will be destructed by their individual deconstructors.
So with RAII and smart pointers the destructors for any pointer members of an object will be called independently of the destructor of the owing object. Raw pointers do not free the memory they point to and have to be deleted manually. Should the constructor of the owning object throw raw pointers will not be freed. This cannot happen with smart pointers.
Consider an exception class with a copy constructor with side-effects.
Can a compiler skip calling the copy constructor here:
try {
throw ugly_exception();
}
catch(ugly_exception) // ignoring the exception, so I'm not naming it
{ }
What about this:
try {
something_that_throws_ugly_exception();
}
catch(ugly_exception) // ignoring the exception, so I'm not naming it
{ }
(yes, I know this is all very ugly, this was inspired by another question)
Yes, it can be elided both during throwing and catching. For catching it can be elided only when the type specified in the catch clause is the same (save for cv-qualifications) as the type of the exception object. For more formal and detailed description see C++11 12.8/31.
...This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
...
in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object
...
when the exception-declaration of an exception handler (Clause 15) declares an object of the same type (except for cv-qualification) as the exception object (15.1), the copy/move operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration.
I think this is specifically permitted. For C++03, 15.1/3 says:
A throw-expression initializes a temporary object, called the
exception object,
and 12/15 says:
when a temporary class object that has not been bound to a reference
(12.2) would be copied to a class object with the same cv-unqualified
type, the copy operation can be omitted by constructing the tempo-
rary object directly into the target of the omitted copy
So, the secret hiding place where in-flight exceptions are kept, is defined by the standard to be a temporary, and hence is valid for copy-elision.
Edit: oops, I've now read further. 15.1/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.
Doesn't get much clearer.
Whether it actually will... if the catch clause were to re-raise the exception (including if it called non-visible code that might do so), then the implementation needs that "temporary object called the exception object" still to be around. So there might be some restrictions on when that copy elision is possible. Clearly an empty catch clause can't re-raise it, though.
Yes. If the catch catches the exception by reference, then there will not be copy (well, that is by definition).
But I think that is not your question, and I believe the code which you've written is written on purpose with no mention of reference. If that is the case, then yes, even in this case, copy can be elided. Actually initialization of the variable in the catch is direct-initialization in theory. And copy in a direct-initialization can be elided by the compiler where it's possible.
C++03 §8.5/14 reads,
[...] In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized;
std::exception requires that its constructor be throw(). Yet std::runtime_error accepts a std::string as its argument, which indicates that it's storing a std::string somewhere. Therefore, an assignment or copy construction has to be going on somewhere. And for std::string, that's not a nothrow operation.
How then does runtime_error::runtime_error meet throw()?
(For context, I'm implementing an exception type, and want to store a few std::strings from the call site, and I want to do it correctly...)
(Here's the same thing in a minimal-ish testcase.)
runtime_error::runtime_error(string const&) doesn't need to meet throw().
It doesn't inherit from or override exception::exception(), and by the time string's copy constructor is invoked, exception::exception() has completed.
If copying the string were to throw an exception, this would unwind runtime_error::runtime_error(string const&) and then, I suppose, invoke exception::~exception().
It's hard to directly show that there is no requirement of a derived ctor to meet a base ctor's exception specifier, but it is strongly implied by the following passage (which describes how the base's destructor is invoked, rather than passing the exception into the base constructor):
[2003: 15.2/2] An object that is partially constructed or
partially destroyed will have destructors executed for all of its
fully constructed subobjects, that is, for subobjects for which the
constructor has completed execution and the destructor has not yet
begun execution. Should a constructor for an element of an automatic
array throw an exception, only the constructed elements of that array
will be destroyed. If the object or array was allocated in a
new-expression, the matching deallocation function (3.7.3.2, 5.3.4,
12.5), if any, is called to free the storage occupied by the object.
The only passage which comes even close to the scenario you presumed (and which I initially presumed) is the following.
[2003: 15.4/3] If a virtual function has an exception-specification,
all declarations, including the definition, of any function that
overrides that virtual function in any derived class shall only allow
exceptions that are allowed by the exception-specification of the base
class virtual function.
But clearly exception::exception() is not a virtual function, and clearly runtime_error::runtime_error(string const&) does not override it.
(Note that this scenario would apply for a virtual destructor; accordingly, you can see that, in libstdc++, runtime_error::~runtime_error() is throw()).
Update, 2015:
Yet std::runtime_error accepts a std::string as its argument, which indicates that it's storing a std::string somewhere. Therefore, an assignment or copy construction has to be going on somewhere. And for std::string, that's not a noexcept operation.
runtime_error (and logic_error) are only required to accept an argument of type std::string const &. They are not required to copy it.
Use these overloads at your own peril. LLVM libc++ does not provide storage.
On the other hand, GNU libstdc++ tiptoes carefully to avoid running out of memory. It copies the contents of the string, but into the exception storage space, not into a new std::string.
Even then, it adds an std::string&& overload and uses friendship to adopt the internal buffer of a std::string argument passed by rvalue, to conserve exception storage space too.
So that's your real answer: "Very carefully, if at all."
You could leverage GCC's generosity by using std::runtime_errors as members of your own exception class, storing one string each. This would still be useless on Clang, though.
Original answer, 2011. This is still true:
An exception during stack unwinding causes terminate to be called.
But constructing the object to be thrown is not part of unwinding, and is treated no differently from the code before the throw expression.
If std::runtime_error::runtime_error( std::string const & ) throws std::bad_alloc, the runtime_error exception is lost (it never existed) and the bad_alloc is handled instead.
Demonstration: http://ideone.com/QYPj3
As for your own class storing std::strings from the call site, you'll want to follow §18.8.1/2:
Each standard library class T that derives from class exception shall have a publicly accessible copy constructor and a publicly accessible copy assignment operator that do not exit with an exception.
This is required because copying from the stack to the thread's exception storage is sensitive to exceptions. §15.1/7:
If the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught, calls a function that exits via an exception, std::terminate is called (15.5.1).
So, you should use a shared_ptr< std::string > or some such to sanitize copies after the first.
Let's say I have class that acts as a "smart pointer" and releases some kind of system resource when destroyed.
class Resource{
protected:
ResourceHandle h;
public:
Resource(ResourceHandle handle)
:h(handle){
}
~Resource(){
if (h)
releaseResourceHandle(h);//external function, probably from OS
}
};
And I have some function that returns value used for initialization of "Resource":
ResourceHandle allocateHandle();
Now, if I do this in my code:
Resource resource(allocateHandle());
AND allocateHandle() throws an exception, what exactly will happen? Will the crash occur during construction of Resource() or before the construction?
Common sense tells me that because exception is thrown before allocateHandle returns, execution won't even enter Resource() constructor, but I'm not exactly sure about it. Is this a correct assumption?
Arguments are evaluated before any function call -- in this case the constructor --.
Therefore, the exception is thrown before the constructor call
Yes you are correct (as everybody else has said).
But what you are alluding to (I think).
What happens to the object if the constructor was entered and the exception is thrown.
Would the destructor still get executed or not?
Destructors are only fired if the constructor actually finished (if an exception is throw that escapes the constructor then the constructor is not finished). In this case the constructor is not entered and thus the object does not exist and therefore the destructor will not be executed.
What happens if the exception is thrown while the constructor is executing.
In this case because the constructor was not completed the destructor will never be executed either, but what about all the member fields? If the constructor is left via an exception then all fully formed members will have their destructors called (A fully formed member is a member whose constructor has been called and completed successfully).
This is a correct assumption.
If the compiler entered the constructor, what value could it pass in from a function that didn't return?
Yes, your assumption is correct.
At this point you are only creating your parameters and pushing them on the stack. The Object of "Resource" is not even formed!
Therefore, an exception will not call the destructor during stack unwinding.