What is the constructor point of std :: function with a custom allocator but no other arguments?

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 
+8
c ++ c ++ 11 std-function
source share
1 answer

std::function Distributor support ... weird.

The current specification for operator=(F&& f) is that it does std::function(std::forward<F>(f)).swap(*this); . As you can see, this means that memory for f is allocated using any std::function by default, and not for the allocator used to build *this . Therefore, the behavior that you observe is correct, although surprising.

Moreover, since the constructors (allocator_arg_t, Allocator) and (allocator_arg_t, Allocator, nullptr_t) noexcept , they cannot store the dispenser even if they want to (erasing the dispenser type may require dynamic allocation). As is, they are basically not operations that exist to support the use-allocator build protocol.

Recently, the LWG rejected a problem that would change this behavior.

+5
source share

All Articles