динамическое приведение — сравнение типов C ++: typeid против двойной отправки dynamic_cast

Существуют ли какие-либо причины для производительности или надежности, чтобы отдавать предпочтение одной над другой?

#include <iostream>
#include <typeinfo>

struct B
{
virtual bool IsType(B const * b) const { return IsType2nd(b) && b->IsType2nd(this); }
virtual bool IsType2nd(B const * b) const { return dynamic_cast<decltype(this)>(b) != nullptr; }
};

struct D0 : B
{
virtual bool IsType(B const * b) const { return IsType2nd(b) && b->IsType2nd(this); }
virtual bool IsType2nd(B const * b) const { return dynamic_cast<decltype(this)>(b) != nullptr; }
};

struct D1 : B
{
virtual bool IsType(B const * b) const { return IsType2nd(b) && b->IsType2nd(this); }
virtual bool IsType2nd(B const * b) const { return dynamic_cast<decltype(this)>(b) != nullptr; }
};

int main()
{
using namespace std;
B b, bb;
D0 d0, dd0;
D1 d1, dd1;

cout << "type B  == type B  : " << (b.IsType(&bb)   ? "true " : "false") << endl;
cout << "type B  == type D0 : " << (b.IsType(&dd0)  ? "true " : "false") << endl;
cout << "type B  == type D1 : " << (b.IsType(&dd1)  ? "true " : "false") << endl;
cout << "type D0 == type B  : " << (d0.IsType(&bb)  ? "true " : "false") << endl;
cout << "type D0 == type D0 : " << (d0.IsType(&dd0) ? "true " : "false") << endl;
cout << "type D0 == type D1 : " << (d0.IsType(&dd1) ? "true " : "false") << endl;
cout << "type D1 == type B  : " << (d1.IsType(&bb)  ? "true " : "false") << endl;
cout << "type D1 == type D0 : " << (d1.IsType(&dd0) ? "true " : "false") << endl;
cout << "type D1 == type D1 : " << (d1.IsType(&dd1) ? "true " : "false") << endl;
cout << endl;
cout << "type B  == type B  : " << (typeid(b) == typeid(bb)   ? "true " : "false") << endl;
cout << "type B  == type D0 : " << (typeid(b) == typeid(dd0)  ? "true " : "false") << endl;
cout << "type B  == type D1 : " << (typeid(b) == typeid(dd1)  ? "true " : "false") << endl;
cout << "type D0 == type B  : " << (typeid(d0) == typeid(&bb) ? "true " : "false") << endl;
cout << "type D0 == type D0 : " << (typeid(d0) == typeid(dd0) ? "true " : "false") << endl;
cout << "type D0 == type D1 : " << (typeid(d0) == typeid(dd1) ? "true " : "false") << endl;
cout << "type D1 == type B  : " << (typeid(d1) == typeid(bb)  ? "true " : "false") << endl;
cout << "type D1 == type D0 : " << (typeid(d1) == typeid(dd0) ? "true " : "false") << endl;
cout << "type D1 == type D1 : " << (typeid(d1) == typeid(dd1) ? "true " : "false") << endl;
}

выход:

type B  == type B  : true
type B  == type D0 : false
type B  == type D1 : false
type D0 == type B  : false
type D0 == type D0 : true
type D0 == type D1 : false
type D1 == type B  : false
type D1 == type D0 : false
type D1 == type D1 : true

type B  == type B  : true
type B  == type D0 : false
type B  == type D1 : false
type D0 == type B  : false
type D0 == type D0 : true
type D0 == type D1 : false
type D1 == type B  : false
type D1 == type D0 : false
type D1 == type D1 : true

4

Решение

С точки зрения дизайна, двойная отправка гораздо более гибкая:

  • В настоящее время вы проверяете строгость равенства между типами с IsType2nd(b) && b->IsType2nd(this), Но может быть в какой-то момент вы хотели бы получить

  • Но однажды вы, возможно, захотите получить дополнительный D1, но все же захотите рассмотреть его так, как если бы он был объектом D1 при сравнении типов. Этот особый случай легко сделать с двойной отправкой.

Такая гибкость обходится дорого: ассемблерный код будет использовать 2 косвенных вызова через виртуальную таблицу плюс динамическое приведение.

По словам Сергея, прямая информация о типах не самая лучшая конструкция: всегда будет строгое сравнение типов без какого-либо особого случая.

Эта гибкость имеет преимущество в простоте генерации кода: код должен просто получать информацию о динамическом типе в начале vtable (и компилятор может легко оптимизировать эту выборку для объекта, тип которого известен во время компиляции).

Ради любопытства, здесь какой-то код сгенерирован : typeid оптимизируется во время компиляции, тогда как двойная диспетчеризация все еще зависит от косвенных вызовов.

1

Другие решения

Как указано в комментариях, следует другое возможное решение, которое не использует ни typeid ни полагается на dynamic_cast,

Я добавил пару дополнительных примеров, чтобы показать, как вы можете легко определить семья тип (например, здесь оба D1 а также D1Bis появляются как принадлежащие к одному и тому же типу семьи, даже если они на самом деле разные типы).
Не уверен, что это желаемая функция, в любом случае …

Надеюсь, это вас интересует.

#include<iostream>

struct BB {
virtual unsigned int GetType() = 0;

bool IsType(BB *other) {
return GetType() == other->GetType();
}

protected:
static unsigned int bbType;
};

unsigned int BB::bbType = 0;

struct B: public BB {
unsigned int GetType() override {
static unsigned int bType = BB::bbType++;
return bType;
}
};

struct D0: public B {
unsigned int GetType() override {
static unsigned int d0Type = BB::bbType++;
return d0Type;
}
};

struct D1: public B {
unsigned int GetType() override {
static unsigned int d1Type = BB::bbType++;
return d1Type;
}
};

struct D1Bis: public D1 { };

int main() {
using namespace std;
B b, bb;
D0 d0, dd0;
D1 d1, dd1;
D1Bis d1Bis;

cout << "type B  == type B  : " << (b.IsType(&bb)   ? "true " : "false") << endl;
cout << "type B  == type D0 : " << (b.IsType(&dd0)  ? "true " : "false") << endl;
cout << "type B  == type D1 : " << (b.IsType(&dd1)  ? "true " : "false") << endl;
cout << "type B  == type D1BIS : " << (b.IsType(&d1Bis)  ? "true " : "false") << endl;
cout << "type D0 == type B  : " << (d0.IsType(&bb)  ? "true " : "false") << endl;
cout << "type D0 == type D0 : " << (d0.IsType(&dd0) ? "true " : "false") << endl;
cout << "type D0 == type D1 : " << (d0.IsType(&dd1) ? "true " : "false") << endl;
cout << "type D0 == type D1BIS : " << (d0.IsType(&d1Bis) ? "true " : "false") << endl;
cout << "type D1 == type B  : " << (d1.IsType(&bb)  ? "true " : "false") << endl;
cout << "type D1 == type D0 : " << (d1.IsType(&dd0) ? "true " : "false") << endl;
cout << "type D1 == type D1 : " << (d1.IsType(&dd1) ? "true " : "false") << endl;
cout << "type D1 == type D1Bis : " << (d1.IsType(&d1Bis) ? "true " : "false") << endl;
}
0

По вопросам рекламы [email protected]