0

我有一个具有单继承的继承层次结构,我想检查指向基址的指针指向的对象是否完全属于派生类型 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 的汇编代码,我们可以看到如下:

-O3 处的 Clang 9.0.0

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

/O2 的 MSVC 19.22 更糟糕,因为它甚至无法内联对 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 在设计上被迫做我在特定上下文中不需要做的事情,例如空指针测试(以及通过异常进行错误处理)或实际上在内存中具有类型信息结构并加载进行比较。

但是,如果 T 不是默认可构造的,则 vtable_ptr 不起作用,如果优化器无法将 T 的实例化编译掉,则可能不会那么快,并且如果 T 的构造函数或析构函数具有副作用,则会以令人惊讶的方式表现。

问题是,有template <typename T> void const * vtable_ptr()没有不需要实例化 T 的实现方式?编译器显然知道此信息。您只需要查看它正在生成的程序集 ( mov eax, offset vtable for Derived+16)。问题是,作为程序员,我可以访问这些信息吗?

4

1 回答 1

0

typeid 操作符可以做你想做的事,如果你认为你可以比编译器做得更好,那么你应该让你自己的编译器对现有的编译器进行“改进”不会给你任何正确性的保证

我也认为你需要std::type_index用于比较typeid

于 2019-11-12T10:54:48.697 回答