type_info c++ c++11 rtti

type_info c++



¿Por qué `std:: make_shared` realiza dos asignaciones separadas con`-fno-rtti`? (3)

#include <memory> struct foo { }; int main() { std::make_shared<foo>(); }

El ensamblaje generado por g++7 y clang++5 con -fno-exceptions -Ofast para el código anterior:

  • Contiene una sola llamada al operator new si no se pasa -fno-rtti .

  • Contiene dos llamadas separadas al operator new si se -fno-rtti .

Esto se puede verificar fácilmente en gcc.godbolt.org ( versión clang++5 ) :

¿Por qué está pasando esto? ¿Por qué la desactivación de RTTI evita que make_shared make_shared el objeto y controle las asignaciones de bloque ?


¿Por qué la desactivación de RTTI evita que make_shared unifique el objeto y controle las asignaciones de bloque?

Se puede ver en el ensamblador (simplemente pegar el texto es realmente preferible tanto para vincular como para tomar fotografías de él) que la versión unificada no asigna un foo simple sino un std::_Sp_counted_ptr_inplace , y además ese tipo tiene un vtable (recuerde que necesita un destructor virtual en general, para hacer frente a eliminaciones personalizadas)

mov QWORD PTR [rax], OFFSET FLAT: vtable for std::_Sp_counted_ptr_inplace<foo, std::allocator<foo>, (__gnu_cxx::_Lock_policy)2>+16

Si deshabilita RTTI, no puede generar el puntero contado in situ porque debe ser virtual.

Tenga en cuenta que la versión no in situ aún se refiere a un vtable, pero parece que simplemente está almacenando directamente la dirección del destructor des-virtualizado.


Naturalmente, std::shared_ptr se implementará asumiendo que el compilador es compatible con rtti . Pero se puede implementar sin ella. Ver shared_ptr sin RTTI? .

Siguiendo el ejemplo de este viejo error libstdc ++ # 42019 de GCC . Podemos ver que Jonathan Wakely agregó una solución para hacer esto posible sin RTTI.

En libstdc ++ de GCC, std::make_shared usa los servicios de std::make_shared que usa un constructor no estándar (como se ve en el código, reproducido a continuación).

Como se ve en este parche, desde la línea 753 , puede ver que obtener el borrado predeterminado simplemente requiere usar los servicios de typeid si RTTI está habilitado , de lo contrario, requiere una asignación separada que no dependa de RTTI.

EDITAR: 9 - Mayo -2017: eliminado el código con derechos de autor publicado anteriormente aquí

No he investigado libcxx , pero quiero creer que hicieron algo similar ...


No hay buena razón. Esto parece un problema de QoI en libstdc ++.

Usando clang 4.0, libc ++ no tiene este problema. , mientras libstdc ++ lo hace .

La implementación de libstdc ++ con RTTI se basa en get_deleter :

void* __p = _M_refcount._M_get_deleter(typeid(__tag)); _M_ptr = static_cast<_Tp*>(__p); __enable_shared_from_this_helper(_M_refcount, _M_ptr, _M_ptr); _M_ptr = static_cast<_Tp*>(__p);

y en general, get_deleter no es posible implementar sin RTTI.

Parece que está utilizando la posición de borrado y la etiqueta para almacenar la T en esta implementación.

Básicamente, la versión get_deleter utiliza get_deleter . get_deleter basó en RTTI. Conseguir que make_shared funcione sin RTTI requería reescribirlo, y tomaron una ruta fácil que hizo que hiciera dos asignaciones.

make_shared unifica los bloques de conteo T y de referencia. Supongo que con los borradores de tamaño variable y las T tamaño variable se vuelven desagradables, por lo que reutilizan el bloque de tamaño variable del almacén para almacenar el T

Un get_deleter modificado (interno) que no hizo RTTI y devolvió un void* podría ser suficiente para hacer lo que necesitan de este eliminador; pero posiblemente no.