为什么`std :: make_shared`用`-fno-rtti`执行两次单独的分配?

时间:2017-03-29 12:26:37

标签: c++ c++11 rtti

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

上述代码g++7clang++5以及-fno-exceptions -Ofast生成的asssembly:

  • 如果operator new

  • ,则包含对-fno-rtti的一次调用。

  • 如果operator new 传递,则两次单独调用包含-fno-rtti

这可以轻松验证on gcc.godbolt.org clang++5 version

screenshot of the above godbolt link with highlighed operator new calls

为什么会这样?为什么禁用RTTI会阻止make_shared统一对象控制块分配?

3 个答案:

答案 0 :(得分:12)

  

为什么禁用RTTI会阻止make_shared统一对象和控制块分配?

您可以从汇编程序中看到(只是粘贴文本非常适合链接和拍照),统一版本不会分配简单的foo而是std::_Sp_counted_ptr_inplace,而且该类型有一个vtable(回想一下它需要一个虚拟的析构函数,以应对自定义删除)

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

如果禁用RTTI,则无法生成就地计数指针,因为它必须是虚拟的。

请注意,非现场版本仍然引用vtable,但它似乎只是直接存储去虚拟化的析构函数地址。

答案 1 :(得分:11)

当然,<?php class td_module_mx16 extends td_module { function __construct($post) { //run the parrent constructor parent::__construct($post); } function render() { ob_start(); ?> <div class="<?php echo $this->get_module_classes();?>"> <div class="meta-info-container "> <div class="td-info-container"> <div class="td-module-image"> <?php echo $this->get_image('td_356x364');?> <?php if (td_util::get_option('tds_category_module_mx16') == 'yes') { echo $this->get_category(); }?> </div> <div class="td-item-details"> <div class="td-module-meta-info "> <?php echo $this->get_date();?> <? // php echo $this->get_category();?> <?php echo $this->get_comments();?> </div> <div class="td-excerpt"> <?php echo $this->get_title(); ?> <?php // echo $this->get_excerpt();?> </div> <? /* <div class="td-read-more"> <a href="<?php echo $this->href;?>"><?php echo __td('Read more', TD_THEME_NAME);?></a> </div> */ ?> </div> </div> </div> </div> <?php return ob_get_clean(); } } 将在假设编译器支持std::shared_ptr的情况下实现。但它可以在没有它的情况下实施。请参阅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所示,您可以看到获取默认删除工具只需要使用rtti 服务(如果已启用RTTI),否则需要单独的分配,不依赖于RTTI。

编辑: 9 - 2017年5月:删除之前发布的受版权保护的代码

我没有调查libcxx,但我想相信他们做了类似的事情......

答案 2 :(得分:6)

没有充分的理由。这看起来像是libstdc ++中的QoI问题。

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

使用RTTI的libstdc ++实现依赖于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);

并且通常,如果没有RTTI,则无法实现get_deleter

它似乎正在使用删除位置和标记在此实现中存储T

基本上,RTTI版本使用get_deleterget_deleter依赖于RTTI。让make_shared在没有RTTI的情况下工作需要重写它,并且他们采取了一条简单的路线,导致它进行两次分配。

make_shared统一T和引用计数块。我想,随着可变大小的删除器和可变大小的T事情变得讨厌,所以他们重用了删除器的变量大小的块来存储T

未执行RTTI并返回get_deleter的已修改(内部)void*可能足以从此删除程序执行所需操作;但可能没有。