Why can't I move an element to a vector not copied?

The compiler tells me that I'm trying to access a remote function (i.e. the copy constructor of a lambda expression). But I do not see where.

std::vector<std::function<void()>> tasks; std::packaged_task<int()> task{ [] { return 1; } }; tasks.emplace_back( [ t = std::move(task) ] () mutable { t(); }); 

(the code is also here )

(I'm trying to find out why they use shared_ptr<task> at https://www.slideshare.net/GlobalLogicUkraine/c11-multithreading-futures ).

In Gcc and MSVC I get the same error - I'm afraid I'm doing something wrong ...

 error: use of deleted function 'main()::<lambda()>::<lambda>(const main()::<lambda()>&)' 

Why can't I replace this std::function with a vector?

+8
c ++ vector move-semantics
source share
3 answers

From cppreference :

F must satisfy Callable and CopyConstructible requirements

Where F is the type of function used to build std::function . However, std::packaged_task does not copy the construct . Thus, in the capture list, t not constructive for copying and is a non-stationary member of the lambda, which makes the implicit copy constructor for the remote lambda.

+4
source share

Short answer: Lambdas and std::packaged_task not std::function s.

Long answer, you cannot move std::packaged_task to std::function

Here is what I suggest as a solution:

 std::vector<std::packaged_task<int()>> tasks; std::packaged_task<int()> task{ [] () mutable { return 1; } }; tasks.emplace_back( std::move(task) ); 

If you really need the std :: function, and not just any callable, you will have to bind the lambda to the std::function

+2
source share

The std::function constructor requires the passed function object to be CopyConstructible , but std::packaged_task<F> not (for any F ). std::function performs type erasure when the dynamic type is not displayed in the static type. Consider, for example:

 int invoke(std::function<int()> f) { return f(); } int main() { std::packaged_task<int()> p{/*etc*/}; auto l = [] { return 5; }; std::function<int()> f( /* either p or l */ ); std::cout << invoke(f) << '\n'; } 

Invoke invocation requires copying F (pass by value). However, F can be copied if it is made from l , but cannot be copied if it is made from p , and this has nothing to do with the static type of F There are three approaches to this problem:

  • Prevent copying std::function at compile time.
  • Allow std::function to be copied at compile time, but throw a runtime error if the contained type is not copied.
  • Allow std::function to be copied at compile time and require that any function object that you put in it be copied.

Approach No. 1 greatly limits the way functions can be stored, transferred, and shared, and basically prohibits common use cases in favor of the unusual case of using an uncopyable functional object.

Approach # 2 is problematic because users need to be trained so that in some cases copying std::function can be unsuccessful and with great diligence when writing code. In addition, if the design requires sharing features, they may need to be wrapped in std::shared_ptr . And if they need to be copied and can be restrained, it gets even worse.

Regardless of how you view approach # 3, it has been standardized. But in light of the above problems, it is also easy to defend.

In fact, I wrote a unique_function class template that uses approach # 1 for my current project, because we often use the option of storing not copied asynchronous task objects, and copying or sharing such tasks is not needed.

0
source share

All Articles