#include <memory>
struct foo { };
int main() { std::make_shared<foo>(); }
Montaż generowany przez zarówno g++7
i clang++5
za pomocą -fno-exceptions -Ofast
dla powyższego kodu:
Zawiera pojedyncze połączenie do
operator new
, jeśli-fno-rtti
jest , a nie minęło.Zawiera dwa oddzielne połączenia do
operator new
, jeśli-fno-rtti
jest przeszedł .
Można to łatwo zweryfikować na gcc.godbolt.org (< href = "https://godbolt.org/g/wikzod" rel = "NefErrer"> clang++5
wersja ) :
Dlaczego to się dzieje? Dlaczego wyłączanie RTTI uniemożliwia make_shared
z ujednolicenia blokady obiekt i alokacje ?
3 odpowiedzi
Nie ma dobrego powodu. Wygląda to jak problem z Qoi w Libstdc ++.
Korzystanie z Clang 4.0, libc ++ nie ma tego problemu., podczas gdy libstdc ++ robi.
Implementacja libstdc ++ z RTTI opiera się na 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);
W ogóle get_deleter
nie można wdrożyć bez RTTI.
Wygląda na to, że korzysta z pozycji Usuwarki i tag, aby zapisać T
w tej implementacji.
Zasadniczo używana wersja RTTI get_deleter
. get_deleter
Opierał się na RTTI. Dostanie make_shared
do pracy bez RTTI
wymagane przepisanie go i wzięli łatnakę, która spowodowała, że wykonuje dwa przydziały.
make_shared
Unika bloków liczenia T
i odniesienia. Przypuszczam, że zarówno z zmiennych kasetami i zmienną wielkości T
rzeczy są paskudne, więc ponownie wykorzystane blok o zmiennej Deletera do przechowywania T
.
Zmodyfikowany (wewnętrzny) {x0}} który nie zrobił RTTI i zwrócił void*
może wystarczyć, aby zrobić to, czego potrzebują z tego deletera; ale prawdopodobnie nie.
Dlaczego wyłączanie RTTI zapobiega ujednoliceniu alokacji obiektu i bloków kontrolnych?
Możesz zobaczyć z asemblera (właśnie wklejając tekst jest naprawdę korzystny zarówno do łączenia, jak i do robienia zdjęć), że ujednolicona wersja nie przydzienia prostego foo
, ale std::_Sp_counted_ptr_inplace
i dalej i dalej Typ ten ma VTable (przypomnij sobie, że potrzebuje wirtualnego destruktora w ogóle, aby poradzić sobie z celownikami niestandardowymi)
mov QWORD PTR [rax], OFFSET FLAT:
vtable for
std::_Sp_counted_ptr_inplace<foo, std::allocator<foo>,
(__gnu_cxx::_Lock_policy)2>+16
Jeśli wyłączysz RTTI, nie może generować wskaźnika wskaźnika, ponieważ to musi być wirtualne.
Należy pamiętać, że wersja bez inwale wciąż odnosi się do VTABLE, ale wydaje się, że wystarczy przechowywać bezpośredni bezpośredni adres destructora.
Naturalnie, std::shared_ptr
zostanie wdrożona przy założeniu kompilatora wspierającego rtti
. Ale może być wdrażany bez niego. Zobacz Sharered_ptr bez RTTI?.
Biorąc wskazówki od tego starego gcc's libstdc ++ # 42019 Błąd. Widzimy, że Jonathan Męski dodał poprawkę, aby to możliwe bez RTTI.
W GCC's Libstdc ++, {X0}} wykorzystuje usługi std::allocated_shared
, który wykorzystuje niestandardowy konstruktor (jak widać w kodzie, odtwarzany poniżej).
Jak widać w tym Patch, z linii 753, można zobaczyć, że pobieranie domyślnego deletera wymaga po prostu korzystanie z usług typeid
, jeśli RTTI jest włączony , w przeciwnym razie , wymaga oddzielnej alokacji , która nie zależy od RTTI.
Edytuj: 9 - maj -2017: Usunięty kod autorski został wcześniej opublikowany tutaj
Nie zbadałem libcxx
, ale Chcę wierzyć, że zrobili podobną rzecz ....
Podobne pytania
Powiązane pytania
Nowe pytania
c++
C ++ to język programowania ogólnego przeznaczenia. Pierwotnie został zaprojektowany jako rozszerzenie C i ma podobną składnię, ale teraz jest to zupełnie inny język. Użyj tego tagu w przypadku pytań dotyczących kodu (który ma zostać) skompilowany za pomocą kompilatora C ++. Użyj znacznika specyficznego dla wersji w przypadku pytań związanych z określoną wersją standardu [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] lub [C ++ 23] itp. .