c++ dynamic_cast of template argument type variable

106 views Asked by At

Why does dynamic casting not work on a vector which specialized by template parameter type?

How can I solve this problem? I would like to determine from a vector whether the elements in it are descendants of a specific class?

class A {};
class B : public A {};

template<class _T_> void func(std::vector<_T_> &vect )
{
    A* pA = dynamic_cast<A*>( vect.data() );
    if( pA != nullptr) { /* I'm happy!*/ }
    else  { /* I'm sad! */   }
}

void main()
{
    std::vector<B> vectorOfB;
    func<B>( vectorOfB );
}
3

There are 3 answers

0
Viktor On BEST ANSWER

Thanks to everyone for the answers! They were very helpful.

It was really a mistake to search for the base class with dynaic_cast.

I found the answer to my problem in Jarod42's post: "Doesn't std::is_base_of<A, T> or std::is_convertible<T*, A*> (or other type_traits) do the job? – Jarod42 Sep 15 at 12:07"

3
Ted Lyngmo On

The vector is empty so it must fail. Compare with this:

B* bp = nullptr;
A* ap = dynamic_cast<A*>(bp);
std::cout << ap << '\n';       // <- 0

Add an element:

std::vector<B> vectorOfB(1);

And then check the element:

template <class T>
void func(std::vector<T>& vect) {
    if(vect.empty()) return; //     Can't check non-existing elements

    A* pA = dynamic_cast<A*>(&vect.front()); // or vect.data()

    if (pA) { /* I'm happy!*/
        std::cout << "happy\n";
    } else { /* I'm sad! */
        std::cout << "sad\n";
    }
}

Demo

0
Eric Foote On

I've had to do something similar before, and the way I've done it might not be optimal, but it did work.

The first thing is to make the function take a vector of pointers, rather than a vector of the object type itself (I recommend using C++ "smart" pointers for this). So the signature would be something like:

void func(std::vector< std::unique_ptr<A> > &vect )

When you're actually constructing the vector in the main function, you can then do something like this:

std::vector< std::unique_ptr<A> > vectorOfB; // Nope, not a typo! 
vectorOfB.push_back(std::make_unique<B>());
func(vectorOfB);

So the next part of this is how do you tell within "func" whether each element is an A or a B. The way I did it was by declaring a virtual function in the parent class like this:

class A
{
    virtual bool isB() const { return false; }
};
class B : public A
{
    virtual bool isB() const { return true; }
};

Admittedly this might not scale well, because if you had lots of classes instead of just two you'd have to have a lot of these functions, but it works.

So now inside "func" you can loop through the vector, and for each index i you can call "vect[i]->isB()" to get a boolean to tell you whether it's an A or a B. If that's all you need to do then you're done, but it's possible that you might want to call some function that the class B has but that class A doesn't. In that case, the following won't work:

if (vect[i]->isB()) {
    vect[i]->someFunctionSpecifictoB();
}

Instead you have to cast it first:

if (vect[i]->isB()) {
    static_cast<B *>(vect[i].get())->someFunctionSpecificToB();
}

Hopefully that's all you need!