I have the following hierarchy:
class base
{
public:
virtual ~base(){}
virtual void foo() {}
};
template <typename T>
class derived1 : public base
{
virtual void foo() {};
};
template <typename T>
class derived2 : public base
{
virtual void foo() {};
};
Now given a pointer to base, I'd like to find out if the
underlying is either derived1 or derived2. The problem is that
both derived1 and derived2 can be specialised on many different
types, using dynamic_cast to test for a down cast requires the
template type to be know. I've ended up with messy, unmaintable and incomplete bit of code:
base* b = new derived1<int>();
if (dynamic_cast<derived1<int>*> ||
dynamic_cast<derived1<unsigned int>*> ||
dynamic_cast<derived1<double>*>)
std::cout << "is derived1";
else if (dynamic_cast<derived2<int>*> ||
dynamic_cast<derived2<unsigned int>*> ||
dynamic_cast<derived2<double>*>)
std::cout << "is derived2";
Is there a better way, that can handle any type specialization?
Move the logic which depends on the type into the type.
Instead of:
if (dynamic_cast<derived1<int>*>(b) ||
dynamic_cast<derived1<unsigned int>*>(b) ||
dynamic_cast<derived1<double>*>(b))
std::cout << "is derived1";
else if (dynamic_cast<derived2<int>*>(b) ||
dynamic_cast<derived2<unsigned int>*>(b) ||
dynamic_cast<derived2<double>*>(b))
std::cout << "is derived2";
add a virtual print_name() const
function to base
, and then do:
void example() {
std::unique_ptr<base> b(new derived1<int>());
b->print_name();
}
class base
{
public:
~base(){}
virtual void foo() {}
virtual void print_name() const = 0;
};
template <typename T>
class derived1 : public base
{
virtual void foo() {}
virtual void print_name() const {
std::cout << "is derived1";
}
};
template <typename T>
class derived2 : public base
{
virtual void foo() {}
virtual void print_name() const {
std::cout << "is derived2";
}
};
Insert a non-templated class inbetween base
and derived1
or derived2
:
class base
{
public:
virtual ~base() {} // **NOTE** Should be virtual
virtual void foo() {}
};
class derived1_base : public base
{
};
template <typename T>
class derived1 : public derived1_base
{
public:
virtual void foo() {}
};
class derived2_base : public base
{
};
template <typename T>
class derived2 : public derived2_base
{
public:
virtual void foo() {}
};
In a comment, you mentioned:
[I want to] call a particular function for each one - btw there's more than
derived1 and derived2
Add that (virtual) function to derived1_base
, and you don't even need to know T
anymore.
if (dynamic_cast<derived1_base*>(foo))
{
std::cout << "is derived1";
dynamic_cast<derived1_base*>(foo)->specific_derived1_function();
}
else if (dynamic_cast<derived2_base*>(foo))
{
std::cout << "is derived2";
dynamic_cast<derived2_base*>(foo)->specific_derived2_function();
}
NOTE: I consider a list of dynamic_cast<>
a code smell, and I urge you to rethink your approach.
You could add a virtual method to do a meta-type check of some kind:
class base
{
public:
~base(){}
virtual void foo() {}
virtual bool isa(const char* type_to_test){
return strcmp(type_to_test,"base")==0;}
};
template <typename T>
class derived1 : public base
{
virtual void foo() {};
virtual bool isa(const char* type_to_test){
return strcmp(type_to_test,"derived1")==0;}
};
Solution 1: add one more virtual function:
enum DerivedType
{
One,
Two,
...
};
class base
{
public:
~base(){}
virtual void foo() {}
virtual DerivedType GetType() = 0;
};
template <typename T>
class derived1 : public base
{
virtual void foo() {};
virtual DerivedType GetType() { return One; }
};
template <typename T>
class derived2 : public base
{
virtual void foo() {};
virtual DerivedType GetType() { return Two; }
};
Solution 2: using tag classes:
class Base
{
public:
virtual ~Base() { }
};
class Derived1Tag
{ };
class Derived2Tag
{ };
template <class T>
class Derived1 : public Base, public Derived1Tag
{ };
template <class T>
class Derived2 : public Base, public Derived2Tag
{ };
int main(int argc, char** argv)
{
Derived1<int> d1;
Derived2<int> d2;
cout << dynamic_cast<Derived1Tag*>((Base*)&d1) << endl;
cout << dynamic_cast<Derived1Tag*>((Base*)&d2) << endl;
return 0;
}