Marko Kitonjics
Marko Kitonjics

Reputation: 257

How to compare class types in C++?

Lets say I've got:

class Base {/*...*/}
class Derived1: public Base {/*...*/}
class Derived2: public Base {/*...*/}

..and I've got:

class SomeClass {
public:
    template<typename DerivedType>
    DerivedType GetDerived();
private:
    vector<Base*> m_classes;
}

In the GetDerived() function I iterate trough the m_classes vector and I would like to do something like:

if(m_classes[i] == DerivedType) {
    return m_classes[i];
}

...and lets assume we know that one of the objects in the m_classes will contain a 'DerivedType' object.

Example:

m_classes[2] = Base* BasePtr = new Derived1;

.. in this case I'd like to use the GetDerived function like this:

GetDerived<Derived1>();

.. and this should return m_classes[2]

How do I do this? How do I compare a base pointer object with a derived class type? More exactly, how should the GetDerived() function look like?

Upvotes: 5

Views: 13206

Answers (5)

Wes Cumberland
Wes Cumberland

Reputation: 1338

As other people have said, you should see if this is really the best way to attack your problem, but dynamic_cast is available in C++. You have to use Run Time Type Identification or RTTI.

Once you've turned it on in your compiler options you can do this inside your loop:

DerivedType* maybeDerived = dynamic_cast<DerivedType>(m_classes[i]);
if (maybeDerived)
     return maybeDerived;

Upvotes: 1

Quentin
Quentin

Reputation: 63124

Storing objects of different classes in the same container when you need to treat them differently is a bad idea.

C++ however has what you're looking for, this time. dynamic_cast<B>(a) will try and convert a (whatever it is) to type B. If the runtime classes do not match, it will return nullptr.

Here is how you could write GetDerived :

template <class Tsearched>
Tsearched *GetDerived() {
    for(auto c : m_classes) {
        if(Tsearched *ptr = dynamic_cast<Tsearched*>(c))
            return ptr;
    }
    return nullptr;
}

Or, with the help of a standard algorithm :

template <class Tsearched>
Tsearched *GetDerived() {
    auto found = std::find_if(m_classes.begin(), m_classes.end(), [](Base *ptr) {
        return dynamic_cast<Tsearched*>(ptr);
    });
    return found == m_classes.end() ? nullptr : static_cast<Tsearched*>(*found);
}

Let me repeat, however, that there is a design issue in here. RTTI (what dynamic_cast uses) is neither elegant, nor fast.

Upvotes: 3

justin
justin

Reputation: 104698

You could use dynamic_cast<>:

template<typename DerivedType>
std::vector<DerivedType*> GetDerived() { // note: return type changed
 std::vector<DerivedType*> result;
 for (DerivedType* at : this->m_classes) {
  if (DerivedType* as = dynamic_cast<DerivedType*>(at)) {
   result.push_back(as);
  }
 }
 return result;
}

dynamic_cast<> requires a Base type with Run Time Type Information (which will exist on types which have virtual functions).

It's worth mentioning that some people dislike dynamic_cast<>, and some conventions/codebases forbid it. It's a tool, and it can be misused.

Upvotes: 2

Johan
Johan

Reputation: 3778

Usually it's not a good idea to do things like that.

You can use dynamic_cast which returns nullptr if the object is not of that type and a pointer to that type otherwise :

if (nullptr != dynamic_cast<DerivedType*>(m_classes[i])) 
{ ... }

Upvotes: 1

AJG85
AJG85

Reputation: 16197

Your example is not valid syntax. You can't have a declaration in the middle of an assignment. However something like http://en.cppreference.com/w/cpp/types/is_base_of can help identify derivations at compile time. As far as identifying derived types at runtime you can use dynamic_cast however ideal solutions should not need to do this and it often means there is a potential design flaw in your objects or the way they are being used.

As pointed out by @PaulMcKenzie you should probably be asking yourself "Why?" before worrying about the how.

Upvotes: 0

Related Questions