2015-09-15 15 views
8

Sto giocando con std :: function e gli allocatori personalizzati ma non si comporta come mi aspettavo quando non fornisco la funzione con un funtore iniziale.Qual è il punto del costruttore std :: function con allocatore personalizzato ma nessun altro argomento?

Quando fornisco un assegnatore personalizzato al costruttore ma nessun functor iniziale, l'allocatore non viene mai utilizzato o così sembra.

Questo è il mio codice.

//Simple functor class that is big to force allocations 
struct Functor128 
{ 
    Functor128() 
    {} 

    char someBytes[128]; 

    void operator()(int something) 
    { 
     cout << "Functor128 Called with value " << something << endl; 
    } 
}; 

int main(int argc, char* argv[]) 
{ 
Allocator<char, 1> myAllocator1; 
Allocator<char, 2> myAllocator2; 
Allocator<char, 3> myAllocator3; 
Functor128 myFunctor; 

cout << "setting up function1" << endl; 
function<void(int)> myFunction1(allocator_arg, myAllocator1, myFunctor); 
myFunction1(7); 

cout << "setting up function2" << endl; 
function<void(int)> myFunction2(allocator_arg, myAllocator2); 
myFunction2 = myFunctor; 
myFunction2(9); 

cout << "setting up function3" << endl; 
function<void(int)> myFunction3(allocator_arg, myAllocator3); 
myFunction3 = myFunction1; 
myFunction3(19); 
} 

uscita:

setting up function1 
Allocator 1 allocating 136 bytes. 
Functor128 Called with value 7 

setting up function2 
Functor128 Called with value 9 

setting up function3 
Allocator 1 allocating 136 bytes. 
Functor128 Called with value 19 

Così case1: MyFunction1 alloca utilizzando allocator1 come previsto.

caso2: myFunction2 viene assegnato a allocator2 nel costruttore, ma quando viene assegnato un functor sembra resettato utilizzando lo std :: allocator predefinito per effettuare l'allocazione. (Quindi nessuna stampa sull'assegnazione).

case3: myFunction3 è assegnato a allocator3 nel costruttore ma quando viene assegnato a myFunction1 l'allocazione avviene utilizzando l'allocatore di function1 per effettuare l'allocazione.

È corretto questo comportamento? In particolare, nel caso 2, perché ripristinare l'utilizzo di default std :: allocator? Se sì, qual è il punto del costruttore vuoto che prende un allocatore in quanto l'allocatore non viene mai utilizzato.

Sto usando VS2013 per questo codice.

classe mia Assegnista è solo un'implementazione minima che utilizza nuovo e si disconnette quando si assegna

template<typename T, int id = 1> 
class Allocator { 
public: 
    // typedefs 
    typedef T value_type; 
    typedef value_type* pointer; 
    typedef const value_type* const_pointer; 
    typedef value_type& reference; 
    typedef const value_type& const_reference; 
    typedef std::size_t size_type; 
    typedef std::ptrdiff_t difference_type; 

public: 
    // convert an allocator<T> to allocator<U> 
    template<typename U> 
    struct rebind { 
     typedef Allocator<U> other; 
    }; 

public: 
    inline Allocator() {} 
    inline ~Allocator() {} 
    inline Allocator(Allocator const&) {} 
    template<typename U> 
    inline Allocator(Allocator<U> const&) {} 

    // address 
    inline pointer address(reference r) { return &r; } 
    inline const_pointer address(const_reference r) { return &r; } 

    // memory allocation 
    inline pointer allocate(size_type cnt, 
     typename std::allocator<void>::const_pointer = 0) 
    { 
     size_t numBytes = cnt * sizeof (T); 
     std::cout << "Allocator " << id << " allocating " << numBytes << " bytes." << std::endl; 
     return reinterpret_cast<pointer>(::operator new(numBytes)); 
    } 
    inline void deallocate(pointer p, size_type) { 
     ::operator delete(p); 
    } 

    // size 
    inline size_type max_size() const { 
     return std::numeric_limits<size_type>::max()/sizeof(T); 
    } 

    // construction/destruction 
    inline void construct(pointer p, const T& t) { new(p)T(t); } 
    inline void destroy(pointer p) { p->~T(); } 

    inline bool operator==(Allocator const&) { return true; } 
    inline bool operator!=(Allocator const& a) { return !operator==(a); } 
}; // end of class Allocator 
+0

C'è una proposta per rimuovere il supporto degli allocatori da 'std :: function': http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0302r0.html con un utile elenco di problemi noti –

risposta

5

s' std::function supporto allocatore è ... strano.

La specifica corrente per operator=(F&& f) è che fa std::function(std::forward<F>(f)).swap(*this);. Come si può vedere, ciò significa che la memoria per f viene allocata utilizzando qualsiasi cosa usi std::function per impostazione predefinita, anziché l'allocatore utilizzato per creare *this. Quindi il comportamento che osservate è corretto, anche se sorprendente.

Inoltre, poiché le (allocator_arg_t, Allocator) e (allocator_arg_t, Allocator, nullptr_t) costruttori sono noexcept, non possono realmente memorizzare l'allocatore anche se volessero (tipo cancellando un allocatore può richiedere un'allocazione dinamica). Così come sono, in pratica, no-op che esistono per supportare il protocollo di costruzione degli use-allocator.

LWG ha recentemente respinto an issue per modificare questo comportamento.

+0

Loro pagano il labbro al protocollo di costruzione usa-allocatore (e 'std :: scoped_allocator_adaptor'), ma in realtà non lo supportano. Forse questo è il motivo per cui GCC sceglie di non implementare il servizio labiale. Sto proponendo una soluzione alternativa: http://bit.ly/allocfun (PDF). – Potatoswatter

+0

E 'stato prescritto che un oggetto richiamabile salvato in 'std :: function' debba essere cancellato dall'assegnatore che usa' std :: function'? O semplicemente usi 'delete p'? – linux40

+0

@ linux40 Gli allocatori non possono eseguire 'construct' o' destroy' su tipi arbitrari dopo la cancellazione del tipo (ma si veda [LWG2502] (http: // wg21.link/LWG2502), che parla di 'construct' ma si applica ugualmente a' destroy'). Ovviamente la deallocazione deve essere eseguita utilizzando l'allocatore; il distruttore verrà chiamato direttamente. –