Переход от базового указателя к шаблонным производным типам - PullRequest
7 голосов
/ 05 марта 2012

У меня есть следующая иерархия:

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() {};
};

Теперь, учитывая указатель на базу, я бы хотел выяснить, базовый является производным1 или производным2. Проблема в том, что производные1 и производные2 могут специализироваться на типов, использование dynamic_cast для проверки приведения вниз требует тип шаблона, который нужно знать. Я закончил с грязным, неосновным и неполным фрагментом кода:

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";

Есть ли лучший способ справиться с любой специализацией типа?

Ответы [ 4 ]

9 голосов
/ 05 марта 2012

Переместите логику, которая зависит от типа, в тип.

Вместо:

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";

добавьте функцию virtual print_name() const к base, а затем выполните:

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";
   }
};
7 голосов
/ 05 марта 2012

Вставьте не шаблонный класс между base и derived1 или 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() {}
};

В комментарии вы упомянули:

[Я хочуto] вызывать определенную функцию для каждой - кстати, есть нечто большее, чем производная1 и производная2

Добавьте эту (виртуальную) функцию к derived1_base, и вам даже не нужно будет знать T.

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();
}

ПРИМЕЧАНИЕ : я рассматриваю список dynamic_cast<> кода, запаха , и я призываю вас переосмыслить свой подход.

3 голосов
/ 05 марта 2012

Вы можете добавить виртуальный метод для проверки мета-типа:

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;}
};
2 голосов
/ 05 марта 2012

Решение 1: добавьте еще одну виртуальную функцию:

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; }
}; 

Решение 2: использование классов тегов:

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...