9

I want to store callbacks in a vector or another container in C++11.

One way to do so would be to store a vector of std::function. This works well for lambda or std::bind with copyable arguments.

However, if there is one non copyable (movable only) argument, it will fail due to the conversion from the lambda/std::bind internal type to the std::function...

#include <vector>

class NonCopyable {
public:
    NonCopyable() = default;
    NonCopyable(const NonCopyable &) = delete;
    NonCopyable(NonCopyable &&) = default;
};

int main() {
    std::vector<std::function<void()>> callbacks;
    callbacks.emplace_back([] {});

    NonCopyable tmp;
    callbacks.emplace_back(std::bind([](const NonCopyable &) {}, std::move(tmp)));
    // When converting the object returned by std::bind to a std::function,
    // a copy of the arguments happen so this code cannot compile.
    return 0;
}

Is there a way to move std::bind arguments into the std::function instead of copying them?

Praetorian
  • 106,671
  • 19
  • 240
  • 328
Thomas Moulard
  • 5,151
  • 2
  • 21
  • 28
  • 3
    `std::function` requires the functor it wraps to be CopyConstructible, so you are out of luck. – T.C. Jan 29 '15 at 07:45
  • So is there a better pattern to store callbacks? Like packaged_task or another class? – Thomas Moulard Jan 29 '15 at 08:22
  • Store pointers to the functions or functors, perhaps? – CinchBlue Jan 29 '15 at 08:33
  • You may be able to use a `std::reference_wrapper`, which you can then put into the `std::vector>` container. – Chris Dodd Jan 29 '15 at 08:35
  • I investigated this (with c++14 lambda captures, custom Functor object) and it looks like the problem is at `std::function`, not at container or at bind. `std::function` can't hold a non-copyable object. http://stackoverflow.com/questions/7944635/storing-noncopyable-but-movable-object-in-stdfunction So you can't use `std::function`. – bolov Jan 29 '15 at 09:30
  • somewhat related: http://stackoverflow.com/questions/20843271/passing-a-non-copyable-closure-object-to-stdfunction-parameter , http://stackoverflow.com/questions/11768273/why-cant-c11-move-a-noncopyable-functor-to-a-stdfunction – bolov Jan 29 '15 at 09:34

2 Answers2

6

std::ref and std::cref are meant to be used in this cases to avoid copying the object (see http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper).

Not sure that I got your question right, but this compiles for me:

#include <vector>
#include <functional>

class NonCopyable {
public:
  NonCopyable() = default;
  NonCopyable(const NonCopyable &) = delete;
  NonCopyable(NonCopyable &&) = default;
};

int main() {
    std::vector<std::function<void()>> callbacks;
    callbacks.emplace_back([] {});

    NonCopyable tmp;
    auto fun = std::bind([](const NonCopyable &) {}, std::cref(tmp));
    callbacks.emplace_back(fun);

    return 0;
}

EDIT: As mentioned in the comments, be careful about the life cycle of the referenced variable!

baol
  • 4,362
  • 34
  • 44
  • 1
    I think he want to store the fun with the tmp and call the fun later. Your code can compile, but the tmp will be freed when he call the fun. Think about change the main() to add_callbacks() and call the callbacks in another function. – alpha Sep 30 '15 at 04:19
  • Yup, your comment in not special to this case though, but it's more a general statement about variables that go out of scope :) – baol Oct 24 '16 at 21:17
0

You could use std::shared_ptr, which is copyable. Something like

using ptr = std::shared_ptr<NonCopyable>;
callbacks.emplace_back(std::bind([](const ptr &) {}, ptr(new NonCopyable())));

This way NonCopyable object will auto destruct on callbacks destructor.