无法使用自定义分配器与allocate_shared/make_shared

yep*_*ons 8 c++ memory-management shared-ptr allocator c++11

在我的C++ 11程序中,我使用shared_ptr<T>了一些主动创建和删除的对象.事实上,标准分配器operator new是一个瓶颈,所以我想创建自己的一个,它将立即分配一堆内存,然后make_shared按需提供.不幸的是,这是我第一次编写分配器,我不知道为什么GCC无法编译以下代码:

#include <memory>

class MyAlloc {
public:
  typedef char* pointer;
  typedef const char* const_pointer;
  typedef char value_type;

  char* allocate(size_t len) {
    return new char[len];
  }

  void deallocate(char *ptr) {
    delete[] ptr;
  }
} my_alloc;

int main() {
  std::allocator_traits<MyAlloc>();
  // MyAlloc is a correct allocator, since allocator_traits can be instantiated
  // If I comment the following line of code, compilation is successful
  std::allocate_shared<int>(my_alloc, 0);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

在这里,我有一个非常简单的存根分配器和一个调用allocate_shared.GCC产生的错误是:

In file included from c:\soft\mingw\lib\gcc\mingw32\4.8.1\include\c++\ext\alloc_traits.h:36:0,
                 from c:\soft\mingw\lib\gcc\mingw32\4.8.1\include\c++\bits\stl_construct.h:61,
                 from c:\soft\mingw\lib\gcc\mingw32\4.8.1\include\c++\memory:64,
                 from a.cpp:1:
c:\soft\mingw\lib\gcc\mingw32\4.8.1\include\c++\bits\alloc_traits.h: In substitution of 'template<class _Alloc> template<class _Tp> using rebind_traits = std::allocator_traits<typename std::__alloctr_rebind<_Alloc, _Tp>::__type> [with _Tp = std::_Sp_counted_ptr_inplace<int, MyAlloc, (__gnu_cxx::_Lock_policy)2u>; _Alloc = MyAlloc]':
c:\soft\mingw\lib\gcc\mingw32\4.8.1\include\c++\bits\shared_ptr_base.h:517:33:   required from 'std::__shared_count<_Lp>::__shared_count(std::_Sp_make_shared_tag, _Tp*, const _Alloc&, _Args&& ...) [with _Tp = int; _Alloc = MyAlloc; _Args = {int}; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]'
c:\soft\mingw\lib\gcc\mingw32\4.8.1\include\c++\bits\shared_ptr_base.h:986:35:   required from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = MyAlloc; _Args = {int}; _Tp = int; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]'
c:\soft\mingw\lib\gcc\mingw32\4.8.1\include\c++\bits\shared_ptr.h:316:64:   required from 'std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = MyAlloc; _Args = {int}; _Tp = int]'
c:\soft\mingw\lib\gcc\mingw32\4.8.1\include\c++\bits\shared_ptr.h:598:39:   required from 'std::shared_ptr<_Tp1> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = int; _Alloc = MyAlloc; _Args = {int}]'
a.cpp:19:40:   required from here
c:\soft\mingw\lib\gcc\mingw32\4.8.1\include\c++\bits\alloc_traits.h:204:66: error: invalid use of incomplete type 'struct std::__alloctr_rebind<MyAlloc, std::_Sp_counted_ptr_inplace<int, MyAlloc, (__gnu_cxx::_Lock_policy)2u>, false>'
         using rebind_traits = allocator_traits<rebind_alloc<_Tp>>;
                                                                  ^
c:\soft\mingw\lib\gcc\mingw32\4.8.1\include\c++\bits\alloc_traits.h:65:12: error: declaration of 'struct std::__alloctr_rebind<MyAlloc, std::_Sp_counted_ptr_inplace<int, MyAlloc, (__gnu_cxx::_Lock_policy)2u>, false>'
     struct __alloctr_rebind;
            ^
Run Code Online (Sandbox Code Playgroud)

为什么会这样?如何正确编写分配器以便它们可以使用allocate_shared?我知道分配器可以支持其他一些运算符和类型特征,但我看不出任何关于GCC想要什么的提示.

此外,有确定以使用charvalue_type这个特定的分配器(会同shared_ptr)或类似的东西void或者shared_ptr<T>::some_weird_stuff是preferrable?

Bra*_*don 14

像这样..你需要它模板化,你需要重新绑定和类型以及分配和释放成员.拥有运营商也很高兴..

#include <memory>

template<typename T>
struct Allocator
{
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef T value_type;

    template<typename U>
    struct rebind {typedef Allocator<U> other;};

    Allocator() throw() {};
    Allocator(const Allocator& other) throw() {};

    template<typename U>
    Allocator(const Allocator<U>& other) throw() {};

    template<typename U>
    Allocator& operator = (const Allocator<U>& other) { return *this; }
    Allocator<T>& operator = (const Allocator& other) { return *this; }
    ~Allocator() {}

    pointer allocate(size_type n, const void* hint = 0)
    {
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }

    void deallocate(T* ptr, size_type n)
    {
        ::operator delete(ptr);
    }
};

template <typename T, typename U>
inline bool operator == (const Allocator<T>&, const Allocator<U>&)
{
    return true;
}

template <typename T, typename U>
inline bool operator != (const Allocator<T>& a, const Allocator<U>& b)
{
    return !(a == b);
}


int main()
{
    std::allocate_shared<int, Allocator<int>>(Allocator<int>(), 0);
}
Run Code Online (Sandbox Code Playgroud)

至少,分配器看起来像:

template<typename T>
struct Allocator
{
    typedef T value_type;

    Allocator() noexcept {};

    template<typename U>
    Allocator(const Allocator<U>& other) throw() {};

    T* allocate(std::size_t n, const void* hint = 0)
    {
        return static_cast<T*>(::operator new(n * sizeof(T)));
    }

    void deallocate(T* ptr, size_type n)
    {
        ::operator delete(ptr);
    }
};

template <typename T, typename U>
inline bool operator == (const Allocator<T>&, const Allocator<U>&)
{
    return true;
}

template <typename T, typename U>
inline bool operator != (const Allocator<T>& a, const Allocator<U>& b)
{
    return !(a == b);
}
Run Code Online (Sandbox Code Playgroud)

这也适用于allocate_shared..但是,作为我的类型,我更喜欢拥有所有功能..即使是所述容器/功能不需要/使用的功能.


Jon*_*ely 12

您的自定义分配器不符合C++分配器要求.

特别是,它不支持反弹以分配不同类型的对象.通常,分配器是模板,在它们为其分配内存的类型上进行参数化.allocate_shared需要重新绑定分配器,以便它可以分配适当大小和类型的内存块,它不希望分配一个char对象数组.

// MyAlloc is a correct allocator, since allocator_traits can be instantiated
Run Code Online (Sandbox Code Playgroud)

这不是一个正确的假设.实例化allocator_traits<MyAlloc>不会实例化其所有成员.

此外,可以将char用作此特定分配器的value_type

这使得你的分配器成为分配器char,但allocate_shared需要一个分配器,some_internal_type_defined_by_the_library因此它试图std::allocator_traits<MyAlloc>::rebind_alloc<some_internal_type_defined_by_the_library>用来获得该类型的分配器,但你的分配器不支持重新绑定要求.

如果您的分配器是表单的模板,MyAlloc<T>allocator_traits可以确定如何将其重新绑定MyAlloc<U>,否则该类型MyAlloc::rebind<U>::other必须有效.

C++标准显示以下作为支持C++分配器类型的最低要求的分配器的示例:


template <class Tp>
struct SimpleAllocator {
  typedef Tp value_type;
  SimpleAllocator(ctor args);
  template <class T> SimpleAllocator(const SimpleAllocator<T>& other);
  Tp* allocate(std::size_t n);
  void deallocate(Tp* p, std::size_t n);
};
template <class T, class U>
bool operator==(const SimpleAllocator<T>&, const SimpleAllocator<U>&);
template <class T, class U>
bool operator!=(const SimpleAllocator<T>&, const SimpleAllocator<U>&);
Run Code Online (Sandbox Code Playgroud)