I play with std :: function and custom allocators, but it doesn't behave as I expected when I don't provide a function with an initial functor.
When I provide an arbitrary allocator to the constructor but have no initial functor, the allocator is never used or seems like that.
This is my code.
//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); }
Output:
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
So, case1: myFunction1 allocates using allocator1, as expected.
case2: myFunction2 is assigned to allocator2 in the constructor, but when a functor is assigned, it appears to reset to use the standard std :: allocator for allocation. (therefore, it does not print out about the distribution).
case3: myFunction3 is provided by allocator3 in the constructor, but when assigned from myFunction1, distribution is done using function1 allocator to perform the allocation.
Is this the right behavior? In particular, in case 2, why return to using the standard std :: allocator? If so, what is the point of the empty constructor that the dispenser accepts, since the dispenser is never used.
I am using VS2013 for this code.
My Allocator class is just a minimal implementation that uses new ones and logs out when it allocates
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