У меня есть иерархия наследования с одиночным наследованием, и я хочу проверить, является ли объект, на который указывает указатель на базу, производным типом T.
Я написал два способа и сравнил ассемблерный код:
template <typename T>
void const * vtable_ptr(T const & t)
{
return reinterpret_cast<void const * const &>(t);
}
template <typename T>
void const * vtable_ptr()
{
T t;
return vtable_ptr(t);
}
struct Base
{
virtual ~Base() = default;
};
struct Derived : Base
{
};
bool test(Base const * p)
{
return vtable_ptr(*p) == vtable_ptr<Derived>();
}
bool test2(Base const * p)
{
return typeid(*p) == typeid(Derived);
}
Если мы сравним ассемблерный код test и test2, то увидим следующее:
Clang 9.0.0 в -O3
test(Base const*): # @test(Base const*)
mov eax, offset vtable for Derived+16
cmp qword ptr [rdi], rax
sete al
ret
test2(Base const*): # @test2(Base const*)
push rax
test rdi, rdi
je .LBB1_7
mov rax, qword ptr [rdi]
mov rax, qword ptr [rax - 8]
mov rdi, qword ptr [rax + 8]
mov eax, offset typeinfo name for Derived
cmp rdi, rax
je .LBB1_2
cmp byte ptr [rdi], 42
jne .LBB1_5
xor eax, eax
pop rcx
ret
.LBB1_2:
mov al, 1
pop rcx
ret
.LBB1_5:
mov esi, offset typeinfo name for Derived
call strcmp
test eax, eax
sete al
pop rcx
ret
.LBB1_7:
call __cxa_bad_typeid
MSVC 19.22 в /O2 еще хуже, так как он даже не может встроить вызов сравнения typeid.
bool test(Base const *) PROC ; test, COMDAT
lea rax, OFFSET FLAT:const Derived::`vftable'
cmp QWORD PTR [rcx], rax
sete al
ret 0
bool test(Base const *) ENDP ; test
p$ = 48
bool test2(Base const *) PROC ; test2, COMDAT
$LN7:
sub rsp, 40 ; 00000028H
call __RTtypeid
lea rdx, OFFSET FLAT:Derived `RTTI Type Descriptor'+8
lea rcx, QWORD PTR [rax+8]
call __std_type_info_compare
test eax, eax
sete al
add rsp, 40 ; 00000028H
ret 0
Проблема, по-видимому, заключается в том, что typeid по замыслу вынужден делать то, что мне не нужно делать в моем конкретном контексте, например проверку нулевого указателя (и обработку ошибок с помощью исключений) или фактическое наличие структуры информации о типе в памяти. и загрузить это для сравнения.
Однако vtable_ptr не работает, если T не является конструируемым по умолчанию, может быть не таким быстрым, если оптимизатор не может скомпилировать экземпляр T, и будет вести себя неожиданным образом, если конструктор или деструктор T имеет побочные эффекты.
Вопрос в том, есть ли способ реализации template <typename T> void const * vtable_ptr()
, который не требует создания экземпляра T? Эта информация, очевидно, известна компилятору. Вам просто нужно посмотреть на сборку, которую он генерирует (mov eax, offset vtable for Derived+16
). Дело в том, есть ли у меня доступ к этой информации как у программиста?
1 ответ
Оператор typeid делает то, что вы хотите, если вы думаете, что можете сделать это лучше, чем это делает компилятор, то вам следует сделать свой собственный компилятор, так как «улучшение» существующих не даст вам никаких гарантий правильности
Также я думаю, что вам нужно использовать std::type_index
для сравнения typeid
Похожие вопросы
Связанные вопросы
Новые вопросы
c++
C++ — это язык программирования общего назначения. Изначально он разрабатывался как расширение C и имел аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде, который будет скомпилирован с помощью компилятора C++. Используйте тег версии для вопросов, связанных с конкретной стандартной версией [C++11], [C++14], [C++17], [C++20] или [C++23]. и т.д.
dynamic_cast
а>. Но вам, вероятно, следует проверить свою архитектуру,dynamic_cast
- это неприятный запах.typeid
- это правильный механизм для проверки точных (а не только той же иерархии) типов, и если он слишком медленный, то да, исправьте запах кода другим способом.