Существуют ли какие-либо причины для производительности или надежности, чтобы отдавать предпочтение одной над другой?
#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
С точки зрения дизайна, двойная отправка гораздо более гибкая:
В настоящее время вы проверяете строгость равенства между типами с IsType2nd(b) && b->IsType2nd(this)
, Но может быть в какой-то момент вы хотели бы получить
Но однажды вы, возможно, захотите получить дополнительный D1, но все же захотите рассмотреть его так, как если бы он был объектом D1 при сравнении типов. Этот особый случай легко сделать с двойной отправкой.
Такая гибкость обходится дорого: ассемблерный код будет использовать 2 косвенных вызова через виртуальную таблицу плюс динамическое приведение.
По словам Сергея, прямая информация о типах не самая лучшая конструкция: всегда будет строгое сравнение типов без какого-либо особого случая.
Эта гибкость имеет преимущество в простоте генерации кода: код должен просто получать информацию о динамическом типе в начале vtable (и компилятор может легко оптимизировать эту выборку для объекта, тип которого известен во время компиляции).
Ради любопытства, здесь какой-то код сгенерирован : typeid оптимизируется во время компиляции, тогда как двойная диспетчеризация все еще зависит от косвенных вызовов.
Как указано в комментариях, следует другое возможное решение, которое не использует ни 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;
}