2017-03-29 51 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. –

相关问题