Three different compilers show three different behaviours compiling this code:
class MyException : public std::exception
{
public:
MyException(std::string str) : m_str(str) {}
virtual const char * what() const throw () {return m_str.c_str(); }
protected:
std::string m_str;
};
Sun C++ 5.8 Patch 121017-22 2010/09/29: Warning Function MyException::~MyException() can throw only the exceptions thrown by the function std::exception::~exception() it overrides
g++ 3.4.3: Error looser throw specifier for `virtual MyException::~MyException()'
Visual Studio 2005: It is very happy (neither error or warning)
class exception {
public:
exception () throw();
exception (const exception&) throw();
exception& operator= (const exception&) throw();
virtual ~exception() throw();
virtual const char* what() const throw();
}
I know what the problem is and how I can fix it:
class MyException : public std::exception
{
public:
MyException(std::string str) : m_str(str) {}
virtual const char * what() const throw () {return m_str.c_str(); }
~MyException() throw() {} <------------ now it is fine!
protected:
std::string m_str;
};
However I am wondering what the standard says in specific situation.
I ran another small test in Visual Studio 2005 and I have found something that really surprise me:
struct Base
{
virtual int foo() const throw() { return 5; }
};
struct Derived : public Base
{
int foo() const { return 6; }
};
int main()
{
Base* b = new Derived;
std::cout << b->foo() << std::endl; //<-- this line print 6!!!
delete b;
}
The signature of the two functions are different. How can this work? It seems that visual studio 2005 completely ignore the exception specification.
struct Base
{
virtual int foo() const throw() { return 5; }
};
struct Derived : public Base
{
int foo() { return 6; } // I have removed the const keyword
// and the signature has changed
};
int main()
{
Base* b = new Derived;
std::cout << b->foo() << std::endl; // <-- this line print 5
delete b;
}
Is this c++ standard? Is there any magic flag to set?
What about VS2008 and VS2010?
Destructors are only called for the completely constructed objects. When the constructor of an object throws an exception, the destructor for that object is not called.
The C++ rule is that you must never throw an exception from a destructor that is being called during the "stack unwinding" process of another exception. For example, if someone says throw Foo(), the stack will be unwound so all the stack frames between the throw Foo() and the } catch (Foo e) { will get popped.
9. How to handle error in the destructor? Explanation: It will not throw an exception from the destructor but it will the process by using terminate() function. 10.
Today I learned that swap is not allowed to throw an exception in C++.
Your program is ill-formed as per the C++ Standard and hence demonstrates a behavior which cannot be explained within the realms of the C++ standard.
Reference:
C++03 Standard:
15.4 Exception specifications [except.spec]
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-specificationof the base class virtual function.
[Example:
struct B
{
virtual void f() throw (int, double);
virtual void g();
};
struct D: B
{
void f(); // ill-formed
void g() throw (int); // OK
};
The declaration of
D::f
is ill-formed because it allows all exceptions, whereasB::f
allows onlyint
anddouble
. ]
It evolved a bit in C++11 [except.spec]:
5/ 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.
So you are never actually allowed to specify a looser exception specification.
However this case is tricky because the destructor is actually synthetized by the compiler itself!
In C++03, I think the Standard was not so careful about those, and you had to write them yourself, in C++11 however we get:
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-idT
if and only ifT
is allowed by the exception-specification of a function directly invoked byf
’s implicit definition;f
shall allow all exceptions if any function it directly invokes allows all exceptions, andf
shall allow no exceptions if every function it directly invokes allows no exceptions.
Where the compiler will generate the exception specification of the destructor so that it matches what can be thrown from the functions it calls (ie, the destructors of the attributes). If those destructors do not throw, then it will generate a noexcept
destructor which will satisfy the base class constraint.
Note: VS2005 is one of the least Standard compliant compiler you might find on Earth.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With