21

To save some code lets say I have a custom allocator named MyAlloc which I have successfully used with a std::vector<int> as follows:

std::vector<int,MyAlloc<int>> vec;

now I want to save a lambda in a std::function using the custom allocator, how do I do it?

My failed attempt:

int i[100];
std::function<void(int)> f(MyAlloc<void/*what to put here?*/>{},[i](int in){
    //...
});

Update: allocators in std::function have been deprecated

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
odinthenerd
  • 5,422
  • 1
  • 32
  • 61
  • I don't see any allocator support [here for std::function](http://en.cppreference.com/w/cpp/utility/functional/function). – RedX Jan 13 '14 at 14:50
  • 1
    @RedX http://en.cppreference.com/w/cpp/utility/functional/function/function suggests that it is possible – odinthenerd Jan 13 '14 at 14:54
  • 2
    @RedX The docs are fine. Note that PorkyBrain linked to the docs for the constructor of `std::function`, while you linked to the class itself. The allocator is only needed for constructing the `std::function`, not for using it. Hence only the constructor is templated on the allocator type, but not the class itself. – ComicSansMS Jan 13 '14 at 15:33
  • 2
    @PorkyBrain Which compiler are you using? I just noticed that VC seems to have [messed up the order of constructor arguments](http://msdn.microsoft.com/en-us/library/bb982007.aspx) here. – ComicSansMS Jan 13 '14 at 15:36
  • @ComicSansMS MSVC2013 – odinthenerd Jan 13 '14 at 16:01
  • 1
    There's a proposal to remove allocator support from `std::function`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0302r0.html with a helpful list of known issues – Andriy Tylychko Jul 31 '17 at 14:39

2 Answers2

25

According to the standard, you need to give a tag type as the first argument to indicate that you want to use a custom allocator:

std::function<void(int)> f(std::allocator_arg, MyAlloc<char>{}, [i](int in){
    //...
});

As pointed out by @Casey and @Potatoswatter in the comments, the template argument type given to the allocator does not matter, as long as it's an object type. So char is fine here.

Update for C++17: It turns out that the allocator support for std::function has a number of fundamental issues, which lead to it being deprecated in C++17. If you nonetheless insist on using it, be sure to carefully check your implementation before doing so. GCC's standard library never implemented those functions, but even if your standard library does, it might not behave the way that you expect.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • Thank you for your help, it seems to work on MSVC2013, are you sure that the template parameter for MyAlloc should be void? – odinthenerd Jan 13 '14 at 15:59
  • 1
    @PorkyBrain: Gut feeling => any type is fine because `std::function` will *rebind* the allocator to another (internal) type anyway. – Matthieu M. Jan 13 '14 at 16:10
  • @PorkyBrain It almost certainly does not matter what parameter you use for the allocator: `std::function` is immediately going to rebind the allocator to whatever internal type it needs to actually allocate, and the allocator you pass to the constructor will only be used to copy-construct that rebound allocator. – Casey Jan 13 '14 at 16:11
  • thanks guys, I thought the same but was not sure. I don't want to end up depending on implementation details of MSVC – odinthenerd Jan 13 '14 at 16:13
  • 3
    @PorkyBrain C++11 §[func.wrap.func.con]/1 states "When any function constructor that takes a first argument of type `allocator_arg_t` is invoked, the second argument shall have a type that conforms to the requirements for Allocator (Table 17.6.3.5). A copy of the allocator argument is used to allocate memory, if necessary, for the internal data structures of the constructed `function` object." So any conforming allocator type is appropriate. – Casey Jan 13 '14 at 16:15
  • 2
    `void` is not a valid allocator type argument. Although `std::allocator` is defined, Allocators in general require object types. Since any arbitrary object type will do, `char` is a better bet. – Potatoswatter Jun 03 '15 at 03:44
  • @Potatoswatter True, `void` is probably not the best choice for the argument type here, considering the specialization for `std::allocator`. Changed the answer accordingly. Thanks for pointing out! – ComicSansMS Jun 03 '15 at 07:07
  • It actually ends up getting removed in C++17. – L. F. Mar 18 '20 at 09:53
1

I realize this was answered properly, but even after reading this article and the replies I struggled a bit getting the syntax correct trying to overload an allocator for std::function that cross compiles on X64, PS4, and Xbox One in VS2012.

If it's not clear to the reader you will need to declare an allocator class per Casey's comment. Although this is fairly obvious if you read all the replies, what wasn't clear was the way these allocators are passed to the object which isn't like most of the STL allocators I've used before which take an allocator type (not instance) as part of the type specification.

For std::function an instantiated allocator is supplied to the constructor of your std::function object, which is what ComicSansMS is showing above.

For using this with a member function instead of the lambda code shown in this sample, it gets a bit tricky:

   #include <functional>

    MyAllocType g_myAlloc; // declared somewhere and globally instantiated to persist

    // sample of a member function that takes an int parameter
    class MyClassType
    {
    public:
        void TestFunction( int param )
        {
        }
    };

    MyClassType MyClass; // instantiated object

    // example without allocator
    // note the pointer to the class type that must precede function parameters since 
    // we are using a method. Also std::mem_fn is require to compile in VS2012 :/
    std::function<void(MyClassType*, int)> f( std::mem_fn( &MyClassType::TestFunction ) );

    // usage of function needs an instantiated object (perhaps there is a way around this?)
    f( &MyClass, 10 );

    // example with allocator
    std::function<void(MyClassType*, int)> f(std::allocator_arg, g_myAlloc, std::mem_fn( &MyClassType::TestFunction ) );

    // usage of function is the same as above and needs an instantiated object 
    f( &MyClass, 10 );

    //or a non member function, which is much cleaner looking
    void NonMemberFunction( int param )
    {
    }

    std::function<void(int)> f(std::allocator_arg, g_myAlloc, NonMemberFunction);

Hope this helps people, it took me longer than I'd like to admit to get this working, and as much as I use this site I figured I'd leave a comment here if for no one other than myself on how to use it. :)

2 final questions to those that are smarter than myself:

Q: Is there a way to include the allocator as part of the type?

Q: Is there a way to use a member function without an instance of an object?

To update this, if you decide to pass one of these std::function objects around as a parameter to some other function, I found I needed to use std::function::assign, or else the assignment results in a shallow copy. This can be a problem if you are trying to pass it along to an object with a longer lifecycle than the original.

Example:

std::function<void(MyClassType*, int)> f(std::allocator_arg, g_myAlloc, std::mem_fn( &MyClassType::TestFunction ) );

void FunctionTakeParam( std::function<void(MyClassType*, int)> &FunctionIn )
{
    // this results in a reallocation using your allocator
    std::function<void(MyClassType*, int)> MyLocalFunction.assign( std::allocator_arg, g_myAlloc, FunctionIn ); 

    // the below results in a shallow copy which will likely cause bad things
    //std::function<void(MyClassType*, int)> MyLocalFunction( std::allocator_arg, g_myAlloc, FunctionIn ); 

    ...
}
jeffdev
  • 31
  • 3
  • There's absolutely no reason to include the allocator in the type, why would you possibly want to do that? One can pass a member function around without an instance of the object, but it can't be called without an object, that wouldn't even make sense. `struct person { void raise_hand(){} };` and then you call `person::raise_hand()`. Who raises their hand? – Mooing Duck Jul 17 '14 at 22:29
  • Why? Because you could have a utility member function that accesses no members but happens to be part of a class. Or a function that is static, not saying it's the typical use case, I had just never seen this syntax before and was curious. Regarding the allocator in the type, it's to prevent the awkwardness that arrives when you start passing these objects around. I can't tell if I pass one of these std::function objects by value will the copy also make use of my allocator? I would assume probably not, so having it in the type decalration can make the usage more clear. – jeffdev Jul 17 '14 at 22:47
  • To try to answer again why I'd want it in the type, this is how I'm overloading the allocator for a std::string typedef std::basic_string, STLHeapAllocator > SampleHeapString; Note that I don't actually instantiate the allocator anywhere. And any object I instantiate of this type uses my allocator right? Or am I crazy and I'm doing it horribly wrong? That is also possible :) – jeffdev Jul 17 '14 at 22:59
  • 3
    oh, is that it? `std::function` (and `std::shared_ptr` and a few others) only allocates a single object on the stack. As such the only other thing it could possible ever need the allocator for is deallocation. As such, it can easily use type erasure for no extra overhead. It still contains an instance of your allocator, but can be given another function with another allocator and it will switch allocators at runtime (with no overhead). So the allocator's type doesn't affect the function's type. – Mooing Duck Jul 17 '14 at 23:21