Динамическое приведение, используемое для проверки типа

Чтобы избежать ситуации X Y, я пытаюсь проверить, допустимо ли приведение указателя void*.

Чтобы решить эту проблему, я хотел использовать свойства dynamic_cast.

Учитывая следующий код:

class A
{
  virtual void foo(){}
};
class B
{
  virtual void bar(){}
};

void* createA() { return new A();}
void* createB() { return new B();}

int main()
{
   A* ptr =reinterpret_cast<A*>(createA());
   A* ptr2 = reinterpret_cast<A*> (createB());

   ptr = dynamic_cast<A*>(ptr); // not NULL as expected
   ptr2 = dynamic_cast<A*> (ptr2); // was expecting NULL, however it's a valid pointer
}

Разве не предполагалось, что dynamic_cast потерпит неудачу с NULL, если RTTI не соответствует ожидаемому типу?


person MichaelCMS    schedule 16.06.2017    source источник
comment
Ваш код заполнен или содержит ошибки. Я исправил их, потому что они очевидны, но в следующий раз, пожалуйста, перепроверьте свой код. Спасибо   -  person Rakete1111    schedule 16.06.2017
comment
Возврат void* в функции, которая может возвращать только один тип указателей, кажется немного странным   -  person Vivick    schedule 16.06.2017
comment
@Vivick это только для примера. Реальный код намного больше.   -  person MichaelCMS    schedule 16.06.2017
comment
Почему вы ожидаете NULL во втором случае? Как только вы использовали reinterpret_cast<>, вы переопределили любую потенциально существующую информацию о типе (которой в любом случае нет с void*).   -  person πάντα ῥεῖ    schedule 16.06.2017
comment
@ πάνταῥεῖ Я думал, что RTTI определяется значениями указателя метода vfptable. Я ошибаюсь, не так ли? Это в соответствии со статически сгенерированной информацией о типе, прикрепленной к типу класса?   -  person MichaelCMS    schedule 16.06.2017
comment
@MichaelCMS void* не имеет vtable или RTTI.   -  person πάντα ῥεῖ    schedule 16.06.2017
comment
@πάνταῥεῖ void* сам по себе ничего не имеет. Однако, если по указанному адресу памяти запускается объект, переинтерпретация адреса памяти позволит получить доступ к виртуальной таблице. Прыгающий RTTI каким-то образом встроен в эту виртуальную таблицу.   -  person MichaelCMS    schedule 16.06.2017
comment
@MichaelCMS Как компилятор должен знать, что ему присвоено void*?   -  person πάντα ῥεῖ    schedule 16.06.2017
comment
Давайте продолжим обсуждение в чате.   -  person MichaelCMS    schedule 16.06.2017
comment
@MichaelCMS Нет.   -  person πάντα ῥεῖ    schedule 16.06.2017


Ответы (2)


Обычный способ решить такие проблемы — дать A и B общий базовый класс/интерфейс тега. void* не сохраняет никакой информации о типе. Также reinterpret_cast<> просто переопределит любую потенциально присутствующую информацию о типе.

Вы можете сделать что-то вроде этого:

class Base {
public:
    virtual ~Base() {}
};
class A : public Base
{
  virtual void foo(){}
};
class B : public Base
{
  virtual void bar(){}
};

Base* createA() { return new A();}
Base* createB() { return new B();}

int main()
{
   Base* ptr = createA();
   Base* ptr2 = createB();

   ptr = dynamic_cast<A*>(ptr); // not NULL as expected
   ptr2 = dynamic_cast<A*> (ptr2); // NULL pointer now
}
person πάντα ῥεῖ    schedule 16.06.2017
comment
Предоставленный вами образец верен и соответствует определению dynamic_cast. Но это подразумевает, что у нас есть указатель, который имеет какую-то информацию о типе. Мне было интересно, есть ли способ проверить тип объекта после полного стирания типа до void*. - person MichaelCMS; 16.06.2017
comment
@MichaelCMS Нет, это невозможно. - person πάντα ῥεῖ; 16.06.2017

dynamic_cast нельзя использовать для проверки того, указывает ли указатель на произвольный адрес памяти на объект определенного типа. dynamic_cast будет выполнять преобразование, проверяя RTTI объекта, на который указывает, только в сценариях, когда тип этого объекта может быть в цепочке наследования типа, к которому приводится. В вашем примере приведение B * к A * всегда будет давать nullptr, даже если B * фактически указывает на A:

A * a_ptr = dynamic_cast<A*>(reinterpret_cast< B* >(createA()));
assert(nullptr == a_ptr);

Более того, вы пытаетесь преобразовать A * в A *, что в основном приводит к noop:

5.2.7 Динамическое приведение [expr.dynamic.cast]

3 Если тип v такой же, как T , или он такой же, как T, за исключением того, что тип объекта класса в T более квалифицирован cv, чем тип объекта класса в v, результатом будет v (преобразованный, если необходимо).

person user7860670    schedule 16.06.2017