#include <memory>
struct foo { };
int main() { std::make_shared<foo>(); }
上述代码g++7
和clang++5
以及-fno-exceptions -Ofast
生成的asssembly:
如果operator new
-fno-rtti
的一次调用。
如果operator new
传递,则两次单独调用包含-fno-rtti
。
这可以轻松验证on gcc.godbolt.org (clang++5
version):
为什么会这样?为什么禁用RTTI会阻止make_shared
统一对象和控制块分配?
答案 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_deleter
。 get_deleter
依赖于RTTI。让make_shared
在没有RTTI
的情况下工作需要重写它,并且他们采取了一条简单的路线,导致它进行两次分配。
make_shared
统一T
和引用计数块。我想,随着可变大小的删除器和可变大小的T
事情变得讨厌,所以他们重用了删除器的变量大小的块来存储T
。
未执行RTTI并返回get_deleter
的已修改(内部)void*
可能足以从此删除程序执行所需操作;但可能没有。