2017-03-29 49 views
25
#include <memory> 
struct foo { }; 
int main() { std::make_shared<foo>(); } 

雙方g++7clang++5-fno-exceptions -Ofast對於上面的代碼所產生的asssembly:爲什麼`std :: make_shared`用`-fno-rtti`執行兩個單獨的分配?

  • 包含對operator new單個呼叫如果是-fno-rtti傳遞

  • 包含兩個單獨的呼叫operator new如果-fno-rtti通過

這可以很容易地驗證on gcc.godbolt.orgclang++5 version

screenshot of the above godbolt link with highlighed operator new calls

這究竟是爲什麼?爲什麼禁用RTTI阻止make_shared統一對象和控制塊分配?

+1

相關:http://stackoverflow.com/questions/38180899/shared-ptr-without-rtti – YSC

+1

由於您已禁用虛擬功能庫不能使用打包結構(元素,refcount和deleter) ,因爲這需要刪除類型。所以庫需要分別分配元素+引用計數和刪除器。 –

+2

這也是一個很好的例子,有些開發人員堅持認爲「沒有rtti +沒有例外會產生最快的C++代碼」。這裏舉一個例子證明rtti實際上可以生成更好的代碼。 –

回答

6

沒有很好的理由。這看起來像libstdC++中的QoI問題。

使用鐺4.0,libc++ does not have this issue.,而libstdc++ does

的的libstdC++實現與RTTI依賴於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); 

,並在一般情況下,get_deleter也不是沒有可能RTTI實現。

看起來它使用刪除位置和標記來存儲T在此實現中。

基本上,RTTI版本使用get_deleterget_deleter依靠RTTI。要求make_shared在不需要RTTI的情況下進行重寫,並且他們採取了一條簡單的路線,使其執行兩次分配。

make_shared統一了T和引用計數塊。我認爲,隨着可變大小的刪除器和可變大小的事物變得討厭,所以他們重新使用刪除者的可變大小塊來存儲T

修改過的(內部的)get_deleter不會執行RTTI並返回void*可能足以執行他們在此刪除程序中所需的操作;但可能不是。

12

爲什麼禁用RTTI阻止make_shared統一對象和控制塊的分配?

您可以從彙編見(只粘貼文本真是最好都鏈接和採取它的照片)是統一版本中不分配的簡單foo而是std::_Sp_counted_ptr_inplace,並進一步指出,該類型有虛表(還記得它需要在一般的虛析構函數,以應對自定義刪除器)

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

如果禁用RTTI,它不能產生就地計數指針,因爲這需要是虛的。

請注意,非inplace版本仍然引用一個vtable,但它似乎只是直接存儲de-virtualized析構函數地址。

+1

*「只是粘貼文字,連接和拍照都比較好」* - 文字在godbolt中可用,它位於快照的正上方。在我看來,在OP中粘貼生成的程序集是一個壞主意,因爲不得不點擊截圖或鏈接比翻閱大量程序集恕我直言更好。此外,這應該被視爲QoI *(「實施質量」)問題嗎? –

+2

是的,文本可以在Godbolt上找到...現在。但是誰知道這個網站還在運行時鏈接是否會中斷?而且,是的,我認爲這是一個QoI實施,因爲它非常慷慨,實施者根本沒有爲no-rtti編寫第二個版本。我不會責怪他們只是說_shared_ptr不能在沒有RTTI_的情況下實現。 – Useless

+0

@VittorioRomeo imgur.com在我工作的地方被過濾;) – YSC

11

當然,std::shared_ptr將假定編譯器支持rtti來實現。但它可以在沒有它的情況下實施。見shared_ptr without RTTI?

從這個舊的GCC的libstdC++ #42019 bug中得到提示。我們可以看到Jonathan Wakely添加了一個修復程序,使其成爲可能,而無需RTTI。

在GCC的libstdC++,std::make_shared uses the services of std::allocated_shared它使用非標準的構造函數(如代碼中所示,轉載如下)。

在本patch, from line 753看到的,你可以看到越來越默認缺失者只需要使用typeid的服務,如果RTTI啓用,否則,就需要單獨分配不依賴於RTTI。

編輯:9 - 五月-2017:除去受版權保護碼之前張貼在這裏

我沒有調查libcxx,但我願意相信他們做了類似的事情....

+0

這是受版權保護的代碼,因此將其張貼在SO上違反了服務條款。該代碼可以在線瀏覽(具有必需的版權標題),以便您可以鏈接到它。 –

+0

@JonathanWakely。哇!我不知道。謝謝(你的)信息。我已經刪除了代碼。我認爲,只要提供了相關的引用和確認,就可以從「OpenSource」許可的庫中免費發佈**片段**。更特別是GLPv3代碼。那麼,我的推定是錯誤的呢? – WhiZTiM

+1

@JonathanWakely我檢查了服務條款。它僅禁止*侵犯*版權的內容。您的立場是否真的認爲,爲了教育目的而採取版權保護措施的免費提供,以及對作品的市場沒有影響的方式侵犯了版權?因爲,如果是這樣,您可能需要查看17 USC 107. –

相關問題