8

Maybe I'm misunderstanding how inheritance works here, but here's my problem:

I have a class Option, and a class RoomOption that derives from it. I have another class Room which holds a vector of shared_ptrs. In main I add a RoomOption to that vector. Then, using typeid() I check the type, and it tells me its an Option. From what I've read, typeid is supposed to return derived types, and shared_ptrs dont cause slicing, so I'm not sure what I'm doing wrong.

Here's the code:

Room.h:

vector<shared_ptr<Option> > options;
void addOption(shared_ptr<Option>);
shared_ptr<Option> getOption(int);

Room.cpp:

void Room::addOption(shared_ptr<Option> option){
    options.push_back(option);
}

shared_ptr<Option> Room::getOption(int i){
    return options[i];
}

main:

shared_ptr<Room> outside(new Room(0, "", ""));
outside->addOption(shared_ptr<RoomOption>(new RoomOption(0, "Go inside", hallway)));
cout<<typeid(player->getRoom()->getOption(0)).name().get()<<endl; 
//This line prints "class std::tr1::shared_ptr<class Option>

It occurs to me that maybe when adding or getting an Option, the RoomOption is casted as an Option due to the return/argument type. If that's the case then how am I supposed to store a vector of more than one type? Or am I getting this all wrong? =\

Jean Finley
  • 507
  • 3
  • 9
  • 21

3 Answers3

18

The typeid works differently for polymorphic (for classes having at least one virtual function) and non-polymorphic types :

  • If the type is polymorphic, the corresponding typeinfo structure which represents it is determined at run-time (the vtable pointer is commonly used for that purpose, but this is an implementation detail)

  • If the type isn't polymorphic, the corresponding typeinfo structure is determined at compile time

In your case, you actually have a polymorphic class Option, but shared_ptr<Option> itsef isn't polymorphic at all. It basically is a container holding an Option*. There is absolutely no inheritance relation between Option and shared_ptr<Option>.

If you want to get the real type, you first need to extract the real pointer from its container using Option* shared_ptr<Option>::get() :

Option * myPtr = player->getRoom()->getOption(0).get();
cout << typeid(*myPtr).name(); << endl;

Or alternatively (it is exactly the same thing) :

Option& myPtr = *player->getRoom()->getOption(0);
cout << typeid(myPtr).name(); << endl;
Frédéric Terrazzoni
  • 2,190
  • 19
  • 25
  • I would remove the reference to the vtable pointer, it's a confusing implementation detail. – Matthieu M. Jul 14 '12 at 14:47
  • Note: you may also simply use `operator*` to get a reference to the underlying `Option` directly, rather than first using `get` and then using `*` on the pointer. – Matthieu M. Jul 14 '12 at 15:48
  • 1
    I wanted to make it clear that the real pointer was stored in the shared_ptr<> instance. Using a reference could let the OP think that shared_ptr<> is a pointer, whereas it's only a container trying to mimic the behavior of a pointer (and that's why typeid() was not working for him). I'll add this as an alternative. – Frédéric Terrazzoni Jul 14 '12 at 15:54
3

First of all yor getting the typeid of the shared_ptr.

Then you should use dynamic_cast instead of typeid. E.g:

if (dynamic_cast<RoomOption*>(player->getRoom()->getOption(0).get()) != 0){
    cout << "Its a RoomOption!" << endl;
}
Sebastian Hoffmann
  • 11,127
  • 7
  • 49
  • 77
  • That worked! Thanks for the example. It's strangely difficult to find a simple example of how to check types with dynamic cast O.o – Jean Finley Jul 14 '12 at 13:20
  • Notice though that a dynmic_cast is considered as a very expensive operation (since your code needs to lookup RTTI informations). Therefore, you shouldnt use it to frequently in time sensitive code. – Sebastian Hoffmann Jul 14 '12 at 13:22
  • @SebastianHoffmann typeid also uses RTTI. – c z Oct 10 '22 at 12:48
1

The type of object a shared_ptr<Option> points to is part of its value, not its type. So this line of code is broken:

cout<<typeid(player->getRoom()->getOption(0)).name()<<endl; 

You want this:

cout<<typeid(player->getRoom()->getOption(0).get()).name()<<endl; 

Or perhaps:

cout<<typeid(*(player->getRoom()->getOption(0))).name()<<endl; 

What typeid does is tell you the actual type of the thing you passed to it. You passed it a shared_ptr<Option>. It doesn't look inside the object to see what it contains.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278