why does the below code compile successfully in VS2010? - c++

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.

Related

Why throwing exception which is a reference calls copy constructor?

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.

Scope of Exception object when caught(using catch) by reference

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.

Why do I always get "terminate called after throwing an instance of..." when throwing in my destructor?

I'm trying to write a unit test that detects an invalid use of the lock() feature of my class. In order to do so, I want to use the destructor and throw an exception from there. Unfortunately, instead of catching the exception, g++ decides to call std::terminate().
There is a very simplified version of the class:
class A
{
public:
A() : f_lock(0) {}
~A() { if(f_lock) throw my_exception("still locked"); }
lock() { ++f_lock; }
unlock() { --f_lock; }
private:
int f_lock;
};
There is a valid test:
A *a = new A;
a->lock();
...
a->unlock();
delete a;
There is the invalid test I'm trying to write:
A *a = new A;
a->lock();
...
bool success = false;
try
{
delete a;
}
catch(my_exception const&)
{
success = true;
}
catch(...)
{
// anything else is a failure
}
if(!success)
{
// test failed...
CPPUNIT_ASSERT(!"test failed");
}
Right now, the delete calls std::terminate() even though the throw is not being called when another exception is active. (i.e. std::uncaught_exception() is false.) And also I clearly am catch all exceptions!
Am I doing something wrong, or is g++ programmed to do that always in destructors?
Update:
The answer by dyp in the comments below works! The following does not directly call std::terminate():
~A() noexcept(false) { throw ...; }
Also for reference about why you do not want a throw in a destructor, this page is excellent;
https://www.securecoding.cert.org/confluence/display/cplusplus/ERR33-CPP.+Destructors+must+not+throw+exceptions
For clarification, there is the full version of the destructor. As we can see I first post a message (it generally goes in your console, may go to a log too). Second I make sure we're not already managing an exception. Finally, I throw an exception named exception_exit which is expected to force a terminate() although in a GUI application you may want to show a MessageBox of some sort to let the user know something happened (since you can capture the Message, you can display that to the user) and then force an application shut down.
Node::~Node() noexcept(false)
{
if(f_lock > 0)
{
// Argh! A throw in a destructor... Yet this is a fatal
// error and it should never ever happen except in our
// unit tests to verify that it does catch such a bug
Message msg(message_level_t::MESSAGE_LEVEL_FATAL, err_code_t::AS_ERR_NOT_ALLOWED);
msg << "a node got deleted while still locked.";
// for security reasons, we do not try to throw another
// exception if the system is already trying to process
// an existing exception
if(std::uncaught_exception())
{
// still we cannot continue...
std::abort();
}
throw exception_exit(1, "a node got deleted while still locked.");
}
}
Also, another detail, you are expected to use the NodeLock object to manage the f_lock flag. That is exception safe since it uses RAII (i.e. a scoped lock). However, at this point I did not want to force the user to make use of the NodeLock to lock/unlock a node, hence this test in the destructor.
In C++11 the noexcept keyword was added. This can be used in function exception specifications:
noexcept(true) is the same as throw(), i.e. this function terminates if anything is thrown
noexcept(false) means the function may throw anything
For most functions, they don't have an exception-specification unless you give them one. A function with no exception-specification may throw anything.
There is a special case for destructors though, found in C++11 [class.dtor]/3:
A declaration of a destructor that does not have an exception-specification is implicitly considered to have the same exception-specification as an implicit declaration (15.4).
The referenced rule, 15.4, says that implicitly-declared special member functions always have an exception-specification. The specification is determined by the following rule, [except.spec]/14:
An implicitly declared special member function (Clause 12) shall have an exception-specification. If f is an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f shall allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions if every function it directly invokes allows no exceptions.
The "function it directly invokes" in this clause means the destructor of any member variable or base class (applied recursively). If there is no such function , then there are no exceptions allowed by such a function, so the default is noexcept(true).
We could summarise the portion of the above quote relating to your code like this:
If all subobjects either have no destructor, or implicitly-generated destructor, or destructor declared as noexcept(true) or equivalent; then this class's destructor defaults to noexcept(true).
So, changing your destructor to have noexcept(false) would reproduce the behaviour of C++03.
In C++03 none of this appeared and your destructor would have defaulted to allowing all exceptions. I don't know for sure why this change was made in C++11, but it may be because it is a bad idea to throw from a destructor unless you really know what you are doing.
Detailed explanation available at http://www.parashift.com/c++-faq/dtors-shouldnt-throw.html. Better explanation than my own words :)
Excerpt :
You can throw an exception in a destructor, but that exception must not leave the destructor; if a destructor exits by emitting an exception, all kinds of bad things are likely to happen because the basic rules of the standard library and the language itself will be violated. Don’t do it

Can I throw a unique_ptr?

I have started using C++ 11 and in particular using unique_ptr liberally to make code exception-safe and ownership more readable. This has generally worked well until I wanted to throw a unique_ptr. I have error code (thrown many places, caught in a single place) that creates complex state. Since the ownership of that dynamically allocated memory logically is being transferred from the thrower to the catcher, unique_ptr seemed like the appropriate type to indicate that and make it clear that the catcher had acquired a heap object. Didn't work, at least with the free Visual Studio 2013. Here's a boiled-down code example that no longer resembles anything useful but elicits the behavior:
// cl /nologo /EHsc /W4 test1.cpp
#include <memory>
using std::unique_ptr;
class IError
{
public:
virtual ~IError() {};
virtual void DoStuff();
};
unique_ptr<IError> Error();
int Foo() { throw Error(); }
int main(void)
{
try {
Foo();
}
catch(unique_ptr<IError> Report)
{
Report->DoStuff();
}
return 0;
}
And the compiler spews thusly:
test1.cpp
test1.cpp(13) : warning C4673: throwing 'std::unique_ptr<IError,std::default_delete<_Ty>>' the following types will n
ot be considered at the catch site
with
[
_Ty=IError
]
test1.cpp(13) : warning C4670: '_Unique_ptr_base<class IError,struct std::default_delete<class IError>,1>' : this bas
e class is inaccessible
test1.cpp(13) : error C2280: 'std::unique_ptr<IError,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,
std::default_delete<_Ty>> &)' : attempting to reference a deleted function
with
[
_Ty=IError
]
C:\bin\Visual Studio Express 2013\VC\INCLUDE\memory(1486) : see declaration of 'std::unique_ptr<IError,std::d
efault_delete<_Ty>>::unique_ptr'
with
[
_Ty=IError
]
Where did I go wrong?
As a surrogate I'll be using Rextester which has MSVC version 18.00.21005.1. For GCC 4.8.1 and Clang 3.5, I'll be using Coliru. Now, initially when giving a hasty answer, I said that unique_ptrs cannot be copied and so you should be catching them by reference. However it appears the error occurs when you throw the object in MSVC. So the above advice will only apply to GCC and Clang.
catch(unique_ptr<IError>& Report)
It appears that they differ in how MSVC handles copy/move elision and/or move semantics, I'm not good enough at C++ to be more specific than that, but let's show some compilable examples. First a basic struct with a deleted copy constructor:
#include <iostream>
struct D {
D() {};
D(const D& other) = delete;
D(D&& other) { std::cout << "call D move constructor... \n"; }
};
int main()
{
try {
throw D();
} catch(D const& d)
{
}
}
Regardless of optimization level, for both GCC and Clang, no output unless you also add -fno-elide-constructors to the invocation and we see that they both call the move constructor. For MSVC, we get this error:
source_file.cpp(22) : error C2280: 'D::D(const D &)' : attempting to reference a deleted function
source_file.cpp(7) : see declaration of 'D::D'
For a more complicated example, see Throwing movable objects. The question is two years old yet we observe the same behavior in that GCC and Clang both call the move constructor in certain situations but MSVC calls the copy constructor in all situations (GCC and Clang differ for the Throw with object not about to die anyhow (enter non-zero integer part.)
Throw directly:
C
caught: 007FFA7C
~
Throw with object about to die anyhow
C
c
~
caught: 007FFA74
~
Throw with object not about to die anyhow (enter non-zero integer)
C
c
caught: 007FFA70
~
1
~
TL;DR GCC and Clang will compile it but MSVC won't. A crappy workaround is to throw a pointer instead:
throw new unique_ptr<IError>;
catch(unique_ptr<IError>* Report);
the common rule with exception is "throw by value, catch by const reference". You can't copy a unique_ptr, I guess that's part of the problem.
Derive your class Error from std::exception, and throw that.
Your error is caused by the fact that std::unique_ptr (deliberately) does not have a copy constructor which the compiler attempts to call when catching the exception.
Note, in your catch clause any compiler must not attempt to move the std::unique object (there cannot be a move from the exception object).
On the one hand,
You can catch your exception by reference - the copying would be avoided in the catch clause. So specifically this error will go away.
Note,
If you catch by value, (since C++11) compilers may perform copy elision in a catch clauses if the copy elision would not change the observable behavior of the program for any reason other than skipping the copy constructor and the destructor of the catch clause argument (for example, if the catch clause argument is modified, and the exception object is rethrown with throw).
This is the case, but since it is a non-mandatory elision, the fact your compiler does not perform it does not violate the language standard.
On the other hand,
A type used for exceptions throwing must have a copy constructor.
Besides the fact that copy elision when both throwing and catching are only permitted but not obligated, there are some cases when copy elision is not sensible.
Even if you factually avoid copying in catch clauses in combination with copy elision or moving when throwing the exception, it is not correct for your type (class) used for exceptions to not have a copy constructor.

Can objects with private copy constructors be thrown?

I've come across some exceptions issue that is unclear to me. In C++, when an object is thrown it is first copied to a temporary object, and the temporary object is then passed to the catching code. The copy involves the use of the object's class copy constructor. AFAIK, this means that if a class has a private copy constructor, it can't be used as an exception. However, in VS2010, the following code compiles and runs:
class Except
{
Except(const Except& other) { i = 2; }
public:
int i;
Except() : i(1) {}
};
int main()
{
try
{
Except ex1;
throw ex1; // private copy constructor is invoked
}
catch (Except& ex2)
{
assert(ex2.i == 2); // assert doesn't yell - ex2.i is indeed 2
}
return 0;
}
Is this legal?
It's not legal. Standard 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. 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).
No, it's not.
15.1.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