2014-03-18 5 views
8

nicht verwenden In meinem C++ 11-Programm verwende ich shared_ptr<T> für einige Objekte, die aktiv erstellt und gelöscht werden. Es ist so passiert, dass Standard-Allokator mit operator new ist ein Engpass, so dass ich eine eigene erstellen möchte, die eine Menge Speicher auf einmal zuzuteilen und dann zu make_shared auf Anfrage geben wird. Leider ist dies das erste Mal, dass ich einen allocator schreiben, und ich habe keine Ahnung, warum GCC nicht in der Lage ist, den folgenden Code zu kompilieren:Kann benutzerdefinierte Zuordner mit allocate_shared/make_shared

#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; 
} 

Hier habe ich sehr einfach Stummel allocator und einen Aufruf an allocate_shared. Der Fehler GCC produziert ist:

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; 
      ^

Warum ist das passiert? Wie schreibe ich Zuweiser korrekt, so dass sie mit allocate_shared arbeiten? Ich weiß, dass es noch einige andere Operatoren und Typmerkmale gibt, die vom Allokator unterstützt werden sollen, aber ich sehe keinen Hinweis darauf, was GCC von mir will. Auch

, ist es OK char als value_type für dieses besondere allocator zu verwenden (in Verbindung mit shared_ptr) oder so etwas wie void oder shared_ptr<T>::some_weird_stuff ist preferrable?

+1

Sie müssen eine 'Rebind'-Member-Vorlage (und einige andere Sachen) bereitstellen. Siehe [allocator.requirements] oder [cppreference: Allocator requirements] (http://en.cppreference.com/w/cpp/concept/Allocator) – dyp

Antwort

12

Like this .. Sie müssen es als Templat, müssen Sie die rebind und die Typen und die Zuweisung und Freigabe von Mitgliedern. Es ist auch schön, die Betreiber zu haben ..

#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); 
} 

zumindest ein allocator aussehen könnte:

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); 
} 

Diese für allocate_shared funktioniert auch .. Allerdings ist die Art von Person, die ich bin, ziehe ich alle Funktionen haben .. Auch die nicht von diesem Behälter/Funktion benötigt/verwendet.

+1

Die meisten dieser Elemente können weggelassen werden, und "allocator_traits" liefert sinnvolle Standardwerte. Selbst Ihre "sehr kurz" Version hat unnötige Typen, "Rebind" wird nicht benötigt, "size_type" und "pointer" werden nur benötigt, weil Sie sie selbst verwenden und der implizit definierte Kopierkonstruktor in Ordnung ist. –

+0

Vielen Dank dafür. Das habe ich nach dem Posten gemerkt. Ich habe meinen Zuordner für pre-C++ 11 im Grunde kopiert und ein wenig modifiziert. Ich habe gerade einen minimalen Zuordner in meinem letzten Schnitt bereitgestellt. Ich denke, Ihre Antwort ist besser und besser geeignet, weil sie beschreibender ist als meine. – Brandon

+0

@CantChooseUsernames Nein, hast du nicht. Aber du kommst näher. 'size_type',' pointer' und 'rebind' entsprechen den Standardeinstellungen von' std :: allocator_traits' und 'operator ==' und 'operator! =' sind erforderlich. (Siehe Jonathan's Antwort;) – Casey

11

Ihr benutzerdefinierter Zuordner erfüllt nicht die C++ - Zuweisungsanforderungen.

Insbesondere unterstützt es nicht das Rebound für die Zuweisung von Objekten eines anderen Typs. Üblicherweise sind Allokatoren Schablonen, die auf den Typ parametriert sind, für den sie Speicher reservieren. allocate_shared muss den Zuordner erneut binden, damit er einen Speicherblock mit der entsprechenden Größe und dem entsprechenden Typ zuweisen kann, der kein Array von Zeichenobjekten zuweisen möchte.

// MyAlloc is a correct allocator, since allocator_traits can be instantiated 

Dies ist keine richtige Annahme. Instanziieren allocator_traits<MyAlloc> instanziiert nicht alle seine Mitglieder.

Auch ist es OK

für diese besondere allocator char als value_type zu verwenden, die Ihr Allocator eines allocator von char machen, aber allocate_shared braucht eine allocator von some_internal_type_defined_by_the_library und so versucht es std::allocator_traits<MyAlloc>::rebind_alloc<some_internal_type_defined_by_the_library> zu verwenden, um Erhalten Sie einen Zuordner für diesen Typ, aber Ihr Zuordner unterstützt die Anforderung zum erneuten Anmelden nicht.

Wenn Ihr allocator eine Vorlage des Formulars ist MyAlloc<T> dann kann allocator_traits bestimmen, wie es zu MyAlloc<U> erneut zu binden, da sonst die Art MyAlloc::rebind<U>::other muss gültig sein.

Die C++ Standard der folgenden als Beispiel eines allocator zeigt die Mindestanforderungen für eine C++ Allocator Typ unterstützt:


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>&); 
+0

Vielen Dank für die detaillierte Beschreibung! – yeputons