12

I'm learning exception handling in C++ and run into a problem. Here's the code:

#include<iostream>
#include<exception>

using namespace std;

class A
{
public:
    virtual void f(void){}
};

class AA:public A
{
public:
    void aa(void){};

};

int main(void)
{

    A a;
    try
    {
        dynamic_cast<AA>(a).aa();
    }
    catch(exception ex)
    {
        cout<<"["<<ex.what()<<"]"<<endl;
    }
    return 0;
}

So I thought the try catch will allow the function to execute and show me the content of the exception, but my compiler does not compile it. I'm using codeblock with GNU GCC. Please help me and show me what I need to do to get the code run as I intended. thanks a lot.

iammilind
  • 68,093
  • 33
  • 169
  • 336
focusHard
  • 271
  • 1
  • 5
  • 12
  • You're getting a compiler error. It's important for you to share that error in your question so you can learn to read and understand them. – Drew Dormann Jun 16 '13 at 02:51
  • sure. "cannot dynamic_cast 'a'(of type 'class A') to type 'class AA'(target is not pointer or reference) – focusHard Jun 16 '13 at 02:53
  • 1
    Which is the main problem here. `dynamic_cast` works on pointer or reference types. `AA` is not a pointer or reference type. – Chad Jun 16 '13 at 02:57
  • Since you're learning exception handling: the best way to catch exception is by const reference - that is `catch(const std::exception& ex)` – milleniumbug Jun 16 '13 at 03:03

4 Answers4

19

dynamic_cast can only cast to a pointer value or reference, which is exactly what the error is telling you.

From $5.2.7/1 of the C++ Standard.

The result of the expression dynamic_cast< T >(v) is the result of converting the expression v to type T. T shall be a pointer or reference to a complete class type, or “pointer to cv void.”

In order for dynamic_cast to throw an exception when the object cannot be converted you need to cast to a reference. Change it to the following:

dynamic_cast<AA&>(a).aa();
//           ^^^ cast to reference.

As Johnsyweb pointed out dynamic_cast will always throw std::bad_cast when the conversion fails. Although std::bad_cast is derived from std::exception it is always a good idea to use the exception which best fits the expected fail condition. This prevents inadvertently interpreting other errors as an unsuccessful cast.

To apply this to your example it might look like the code below.

#include <iostream>
#include <typeinfo> // std::bad_cast

class A
{
public:
    virtual void f(void){}
};

class AA:public A
{
public:
    void aa(void){};
};

int main(void)
{
    A a;

    try
    {
        dynamic_cast<AA&>(a).aa();
    }
    catch(const std::bad_cast& ex)
    {
        std::cout << "["<<ex.what()<<"]" << std::endl;
    }
    return 0;
}

[Note, doing things like using namespace std; is strongly discouraged as it can cause conflicts with identifiers in the global namespace. I have removed it in the example above.]

Community
  • 1
  • 1
Captain Obvlious
  • 19,754
  • 5
  • 44
  • 74
  • after modifying my code to , the program outputs [std::exception]. how do I get to std::bad_cast? can you please illustrate "it is always a good idea to use the exception which best fits the expected fail condition" using code example? thanks – focusHard Jun 16 '13 at 03:16
  • @focusHard, put a `catch(std::bad_cast& e)` above the existing `catch()` block. And `#include`. – iammilind Jun 16 '13 at 03:23
  • 2
    @focusHard `bad_cast` is in the `typeinfo` header. I have updated my answer to include the necessary changes to your example. Another thing you should do is start using [cppreference.com](http://en.cppreference.com/w/cpp) as a reference. It gives lists the relevant header files and usually provides examples. It can be a great help when learning C++. – Captain Obvlious Jun 16 '13 at 03:25
5

You are getting a compiler error because your dynamic_cast is not on pointer or reference.
Change it to:

dynamic_cast<AA&>(a).aa();

... and you get the proper exception thrown.

On side note: Smart compilers like g++ would warn as well:
warning: dynamic_cast on an object (here a) can never succeed.

So it's better to limit such code for toying around. In production quality code, the dynamic_cast should be performed only on pointer/reference.

iammilind
  • 68,093
  • 33
  • 169
  • 336
5

Your problem is not with exception handling, but with your dynamic cast:

'AA' is not a reference or pointer

dynamic_cast safely converts pointers and references to classes and not instances.

So you could do:

dynamic_cast<AA&>(a).aa();

...which will always fail and throw a std::bad_cast exception.

You should catch the most-specific type of exception that you are expecting and since the recommended way to catch is by reference, you should prefer:

catch (std::bad_cast const& ex)

Further reading: dynamic_cast conversion on cppreference.com.

Community
  • 1
  • 1
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
  • I tried catch(std::bad_cast const& ex) as you suggested but get several error messages. "expected type-specifier","expected unqualified-id before 'const'" – focusHard Jun 16 '13 at 03:26
  • 1
    `std::bad_cast` is defined in header ``. You'll need to `#include` this. – johnsyweb Jun 16 '13 at 03:28
5

I just dealt with the same error, but in my case I was going from a pointer to a pointer, so the other answers here did not apply. My error message was slightly different, however: error: cannot dynamic_cast 'f()' (of type 'class B*') to type 'class A*' (target is not pointer or reference to complete type).

The root cause in my case was much more simple and mundane.

Notice the addition of to complete type at the end. This caused me to remember that I did not include the header file for my class which I was using. It was not an unknown symbol because A* was forward declared with class A; in the header file, causing it to exist but not be complete, hence the error.

The solution in my case was to include the header file for the class I was casting to.

This is not the question asker's problem above, but as can be seen by my case can generate the same type of error.

Loduwijk
  • 1,950
  • 1
  • 16
  • 28