0

I am attempting to write a class which can store a movable (but non-copyable) callable object as a member variable. I understand that std::function must be copyable (although I don't understand the motivation behind this requirement), I'm looking for advice on if I can make this work some other way. see simplified code below which uses std::function and does not compile:

#include <iostream>
#include <memory>
#include <functional>

class Worker {
 public:
    using Callback = std::function<void()>;

    template <typename Cb>
    void DoAsyncWork(Cb&& cb) {
        cb_ = std::forward<Cb>(cb);
        // initiate some asynchronous code to be completed later
    }

    // after the asynchronous work has completed
    void OnWorkComplete() {
        cb_();
    }

 private:
    Callback cb_;
};

int main() {
    Worker worker;
    auto x = std::make_unique<int>(123);

    // i want to give ownership of x to the lambda
    worker.DoAsyncWork([x = std::move(x)]() mutable { std::cout << *x << std::endl; });
}

This code does not compile with g++ 8.1.0:

g++ -std=c++17 main.cpp

In file included from /usr/local/include/c++/8.1.0/functional:59,
                 from async.cpp:3:
/usr/local/include/c++/8.1.0/bits/std_function.h: In instantiation of ‘static void std::_Function_base::_Base_manager<_Functor>::_M_clone(std::_Any_data&, const std::_Any_data&, std::false_type) [with _Functor = main()::<lambda()>; std::false_type = std::integral_constant<bool, false>]’:
/usr/local/include/c++/8.1.0/bits/std_function.h:208:16:   required from ‘static bool std::_Function_base::_Base_manager<_Functor>::_M_manager(std::_Any_data&, const std::_Any_data&, std::_Manager_operation) [with _Functor = main()::<lambda()>]’
/usr/local/include/c++/8.1.0/bits/std_function.h:676:19:   required from ‘std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = main()::<lambda()>; <template-parameter-2-2> = void; <template-parameter-2-3> = void; _Res = void; _ArgTypes = {}]’
/usr/local/include/c++/8.1.0/bits/std_function.h:524:4:   required from ‘std::function<_Res(_ArgTypes ...)>::_Requires<std::function<_Res(_ArgTypes ...)>::_Callable<typename std::decay<_U1>::type>, std::function<_Res(_ArgTypes ...)>&> std::function<_Res(_ArgTypes ...)>::operator=(_Functor&&) [with _Functor = main()::<lambda()>; _Res = void; _ArgTypes = {}; std::function<_Res(_ArgTypes ...)>::_Requires<std::function<_Res(_ArgTypes ...)>::_Callable<typename std::decay<_U1>::type>, std::function<_Res(_ArgTypes ...)>&> = std::function<void()>&]’
async.cpp:11:13:   required from ‘void Worker::DoAsyncWork(Cb&&) [with Cb = main()::<lambda()>]’
async.cpp:29:86:   required from here
/usr/local/include/c++/8.1.0/bits/std_function.h:173:6: error: use of deleted function ‘main()::<lambda()>::<lambda>(const main()::<lambda()>&)’
      new _Functor(*__source._M_access<_Functor*>());
      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
async.cpp:29:41: note: ‘main()::<lambda()>::<lambda>(const main()::<lambda()>&)’ is implicitly deleted because the default definition would be ill-formed:
     worker.DoAsyncWork([x = std::move(x)]() mutable { std::cout << *x << std::endl; });
                                         ^
async.cpp:29:41: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
In file included from /usr/local/include/c++/8.1.0/memory:80,
                 from async.cpp:2:
/usr/local/include/c++/8.1.0/bits/unique_ptr.h:394:7: note: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^~~~~~~~~~

Is there any way I can pass ownership of the unique_ptr into the lambda and store it as a class member of Worker? Thanks in advance

miles.sherman
  • 171
  • 3
  • 11
  • 1
    "*although I don't understand the motivation behind this requirement*" Most things that take callable objects feel free to copy from them. – Nicol Bolas Apr 12 '20 at 22:09
  • in particular standard algorithms that take function objects are explicitly permitted to copy them: http://eel.is/c++draft/algorithms.requirements#10.sentence-1 – 463035818_is_not_an_ai Apr 12 '20 at 22:26
  • @idclev thanks for pointing this out in the spec. I guess I still don't understand why they made that a requirement, while many other stl containers allow either copy or move. passing ownership to a callable object is a common idiom – miles.sherman Apr 13 '20 at 01:02
  • nevermind, i see the linked page that Nathan associated this with (thanks nathan). it seems that the copyable requirement is associated with std::function::swap() – miles.sherman Apr 13 '20 at 01:04
  • consider parallel execution of an algorithm, then each thread wants his own copy of the function to be called. Elements of a container are different because in general you cannot assume them to be leightweight – 463035818_is_not_an_ai Apr 13 '20 at 06:43

0 Answers0