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{}; auto l = [] { return 5; }; std::function<int()> f( ); 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.
Arne vogel
source share