提升池分配器不会在 g++ 中使用 std::allocate_shared 进行编译

Boost pool allocators won't compile with std::allocate_shared in g++

本文关键字:allocate std shared 编译 分配器 g++      更新时间:2023-10-16

编辑:

澄清我想要的结果,因为我没有很好地沟通:
能够使用std::allocate_sharedboost::fast_pool_allocator作为使用g++4.8或更高版本的分配方法,使用boost 1.56.0。目前,这在g++4.6上有效,在4.7、4.8和4.9上失败。

需要明确的是,我不希望有这个工作g++4.7。

产生错误的测试代码:

#include "boost/pool/pool.hpp"
#include "boost/pool/pool_alloc.hpp"
#include <memory>
int main(int argc, char** argv)
{
  auto fails = std::allocate_shared<int>( boost::fast_pool_allocator<int>() );
  auto works = std::allocate_shared<int>(boost::fast_pool_allocator<int>(), 5);
}

在我们的代码库中,我们将std::allocate_shared与提升池分配器结合使用,这会导致一些严重的编译错误。然而,这在g++的不同版本中发生了变化:
详细信息:64位,(4.7,4.8)-std=c++11,(4.6)-std=c++0x,boost 1.56.0
4.6-编译愉快
4.7-破坏编译器

内部编译器错误:重新输入错误报告例程。请提交一份完整的错误报告,如果合适的话,提供经过预处理的源代码。看见以获取说明。预处理的源存储在/tmp/cca0Emq9.out文件中,请附上这是你的错误报告。

4.8-讨厌的编译错误

/XXXXXXXXXX/boost/boost/pool/pool_alloc.hpp:399:

错误:使用已删除的函数'std::_Sp_counted_prt_inplace,(__gnu_cxx::_Lock_policy)2u>::_Sp_counted_ptr_inplace(conststd::_Sp_counted_ptr_inplace,(__gnu_cxx::_Lock_policy)2u>&)'{new(ptr)T(T);}^

/usr/include/c++/4.8/bits/shared_ptr_base.h:198:错误:'std::_Sp_counted_base&lt_Lp>::_Sp_counted_base(常量std::_Sp_counted_base&lt_Lp>&)[带有__gnu_cxx::_Lock_policy_Lp=(__gnu_cxx::_Lock_policy)2u]'是私有的_Sp_counted_base(_Sp_counted_base const&)=删除;^/usr/include/c++/4.8/bits/shared_ptr_base.h:379:错误:在此上下文class_Sp_counted_ptr_inplace final:public_Sp_counted _base&lt_Lp>^

/usr/include/c++/4.8/bits/shared_ptr_base.h:379:错误:使用已删除的函数'std::_Sp_counted_base&lt_Lp>::_Sp_counted_base(常量std::_Sp_counted_base&lt_Lp>&)[带有__gnu_cxx::_Lock_policy_Lp=(__gnu_cxx::_Lock_policy)2u]'

/usr/include/c++/4.8/bits/shared_ptr_base.h:198:错误:在此处声明_Sp_counted_base(_Sp_counted_base const&)=删除;^

4.9-讨厌的编译错误(略有不同)

/XXXXXXXXXXXXX/boost/boost/pool/pool_alloc.hpp:399:错误:使用删除了函数'std::_Sp_counted_prt_inplace,(__gnu_cxx::_Lock_policy)2u>::_Sp_counted_ptr_inplace(conststd::_Sp_counted_ptr_inplace,(__gnu_cxx::_Lock_policy)2u>&)'{new(ptr)T(T);}^

/usr/include/c++/4.9/bits/shared_ptr_base.h:203:错误:'std::_Sp_counted_base&lt_Lp>::_Sp_counted_base(常量std::_Sp_counted_base&lt_Lp>&)[带有__gnu_cxx::_Lock_policy_Lp=(__gnu_cxx::_Lock_policy)2u]'是私有的_Sp_counted_base(_Sp_counted_base const&)=删除;^

/usr/include/c++/4.9/bits/shared_ptr_base.h:494:错误:在此上下文class_Sp_counted_ptr_inplace final:public_Sp_counted _base&lt_Lp>^

/usr/include/c++/4.9/bits/shared_ptr_base.h:494:错误:使用已删除的函数'std::_Sp_counted_base&lt_Lp>::_Sp_counted_base(常量std::_Sp_counted_base&lt_Lp>&)[带有__gnu_cxx::_Lock_policy_Lp=(__gnu_cxx::_Lock_policy)2u]'

我花了大量的时间试图弄清真相,我会得到一些帮助如果有人更熟悉这些组件。

我花了很多时间研究不同的编译器版本,假设这是g++4.7中的崩溃和其他回答者/评论者的评论所暗示的编译器错误。然而,在回到编译错误并深入研究了一段时间后,我终于在一定程度上具体了解了编译错误的原因。

因此,问题确实存在于boost::fast_pool_allocatorboost::pool_allocator中,回顾起来似乎有点明显。问题的基本症结在于,boost分配器构造方法是根据c++98标准分配器规范实现的,并且具有采用单个常量&用于在新位置复制构造对象的param。c++11样式使用可变模板,参数通过placementnew传递给正在创建的对象的构造函数。在我的测试代码的特定情况下,导致错误的是没有将初始化值传递给std::allocate_shared的变体。当前的核心问题是,c++11std::allocate_shared试图将可变数量的参数传递给只需要一个参数的construct()方法。在我的测试中,当正好有一个值传入并且没有为其他变体调用时,我可以中断构造方法。例如,如果您正在分配std::pair<>(2个参数),则根本不调用构造方法,必须使用其他机制。我对此进行了跟踪,看起来std::allocate_shared在内部封装了构造调用,如果对构造的调用不匹配,则调用一个替代方法(通过隐式函数查找),该方法直接构造对象。下面最上面的方法调用分配器的construct()方法,下面一个直接新的对象:

alloc_traits.h:250-61

template<typename _Tp, typename... _Args>
static typename
    enable_if<__construct_helper<_Tp, _Args...>::value, void>::type
    _S_construct(_Alloc& __a, _Tp* __p, _Args&&... __args)
{ __a.construct(__p, std::forward<_Args>(__args)...); }
template<typename _Tp, typename... _Args>
static typename
enable_if<__and_<__not_<__construct_helper<_Tp, _Args...>>,
         is_constructible<_Tp, _Args...>>::value, void>::type
    _S_construct(_Alloc&, _Tp* __p, _Args&&... __args)
{ ::new((void*)__p) _Tp(std::forward<_Args>(__args)...); }

这是我真正确定编译错误来源的时间,但这足以让我为这个问题找到一个简单有效的解决方案。

这里的解决方案很简单;boost需要更新以使用新的c++11分配器规范。这样做实际上非常简单;在pool_alloc.hpp中替换的所有实例

void construct(const pointer ptr, const value_type & t)
{ new (ptr) T(t); }

带有

template <typename... Args>
void construct(const pointer ptr, Args&&... args)
{
  new (ptr) T( std::forward<Args>(args)... );
}

这似乎是一个bug,boost没有更新他们的代码以支持c++11,但g++5.0(我确认了这一点)编译没有问题,这意味着添加这种支持并不是强制性的。std::allocate_shared可能是为了与旧的分配器接口向后兼容,4.7、4.8和4.9上的崩溃和编译错误就是这种支持被破坏的原因。我会在boost bug跟踪器上发布一张票证,看看他们认为交易是什么:boost trac票证

由于这似乎是libstdc++的问题(?),您可以使用标准库函数的boost等价物来代替boost::allocate_shared。包括<boost/make_shared.hpp>。这里有一个Coliru演示,显示了四个编译良好的不同GCC版本。正如我在评论中提到的,std::allocate_shared使用GCC的主干构建和libc++。我找不到与此问题相关的现有错误报告,但您可以为GCC或Boost提交一份。