dynamic_cast без RTTI - PullRequest
       27

dynamic_cast без RTTI

0 голосов
/ 26 октября 2018

У меня есть структура, подобная следующей:

struct managed_object {
  virtual ~managed_object() { }
};

class trait1 {
  public: 
    virtual void myMethod() const = 0;
};

class trait2 {
  public: 
    virtual void myOtherMethod(int x) const = 0;
};

class MyType final : public managed_object, public trait1 {
 ...
};

class MyType2 final : public managed_object, public trait1, public trait2 {
 ...
};

class wrapper {
  private:
    managed_object* ptr;
  public:
    template<typename T> T* object() const { 
      return dynamic_cast<T*>(data.ptr); 
    }
};

Так что в основном у меня есть базовый класс managed_object, от которого наследуются несколько типов.Каждый из этих подтипов может наследоваться от любой комбинации признаков, и они final, так что я уверен, что у них не будет более глубокого уровня наследования.

Код работает благодаря RTTI, который принимает нагрузкусклеивать все вместе, но по цене, иначе

wrapper w = ...
trait* asTrait1 = w.object<trait1>;

не сработает, потому что нет прямой связи между типами managed_object и trait1.

В моем полном коде I 'Я уже уверен, что все dynamic_cast не потерпит неудачу, потому что у меня есть дополнительные данные (не показанные в примере), которые предоставляют мне некий RTTI, необходимый для других частей кода.

Учитывая это,Существует ли общая схема для решения проблемы side downcast без использования dynamic_cast и RTTI, если я уже знаю, что класс MyType наследуется от определенного trait?Я пытаюсь найти умное решение, так как это тяжелое узкое место кода.

Ответы [ 2 ]

0 голосов
/ 26 октября 2018

Перво-наперво: вы должны использовать static_cast. reinterpret_cast не очень подходит для этого.

Но чтобы приведение в действие сработало, ваша программа должна знать, куда она движется. Под этим я подразумеваю, что он должен знать путь, который должен пройти, чтобы разыграть от A до B. Если A и B находятся в одной иерархии классов, это просто: просто следуйте указанной иерархии классов, чтобы выполнить приведение.

Но если у вас есть:

struct C: A, B {};

И это единственное отношение между A и B, static_cast не может узнать о C (это та информация, которую предоставляет RTTI), и, следовательно, он не может выполнять приведение, поскольку A и B на самом деле не связаны.

Чтобы указать этот путь, вы должны разрешить своей программе как-то это узнать. Самый простой способ - использовать шаблон wrapper:

template<typename T>
class wrapper {
  managed_object* ptr;
public:
  template<typename Trait> Trait* object() const { 
    return static_cast<Trait*>(static_cast<T*>(ptr)); 
  }
};

Тогда:

MyType a;
wrapper<MyType> w{&a};
trait1* asTrait1 = w.object<trait1>(); // OK

Обратите внимание, что я точно говорю, как выполнять приведение, сначала понижая до производного типа, а затем "возвращая обратно" к признаку.

Примечание о reinterpret_cast

Если вы преобразуете класс в его Base (MyType в trait1), dynamic_cast вернет указатель или ссылку на подобъект базового класса в производном объекте (static_cast также может сделать это преобразование правильно). Это означает, что значение возвращаемого указателя может фактически отличаться от значения предоставленного указателя. reinterpret_cast никогда не внесет такое изменение в значение указателя. Он будет просто переосмысливать то, что ему передано, как новый тип, что явно неверно. Очевидный вывод - не использовать reinterpret_cast для выполнения приведения в иерархиях классов.

0 голосов
/ 26 октября 2018

Вы не можете использовать dynamic_cast без RTTI.За исключением нескольких угловых случаев.

Вы можете использовать static_cast или reinterpret_cast (пожалуйста, не), но тогда это будет вас , если выпоймите это неправильно - тогда вы больше не сможете проверять nullptr, чтобы увидеть, успешно ли приведен

...