-1

Here is a simple code snippet:

#include <thread>
#include <future>
#include <functional>

void foo(int){}

int main()
{
    std::thread(foo, 1).join();                  //works indeed
    std::packaged_task<void(int)> task{foo, 1};  //complian
    std::packaged_task<void(int)> task{std::bind(foo, 1)};
}

Both std::thread() and std::packaged_task() accept callable targets, why the code for std::packaged_task() is a little different? Why does not make the std::packged_task() works like std::thread()(i.e. accepts arguments like std::thread)?

The class template std::packaged_task wraps any Callable target (function, lambda expression, bind expression, or another function object) so that it can be invoked asynchronously. Its return value or exception thrown is stored in a shared state which can be accessed through std::future objects.

John
  • 2,963
  • 11
  • 33
  • 2
    The code is different because the contexts of execution are different. [`std::packaged_task`](https://en.cppreference.com/w/cpp/thread/packaged_task) is a template; [`std:;thread`](https://en.cppreference.com/w/cpp/thread/thread) is *not* a template. For `std::thread`, the callable type is *deduced*. For `std::packaged_task<>` that type is *specified* as a template argument. If you look at the [ctors for `std::packaged_task`](https://en.cppreference.com/w/cpp/thread/packaged_task/packaged_task) you'll find none which assemble the callable via perfect forwarding. Thus, your `bind` (or lambda). – WhozCraig Apr 19 '22 at 03:57
  • @WhozCraig I think your comment worth a great answer. – Louis Go Apr 19 '22 at 03:59
  • @WhozCraig Could you please explain that in more detail? Both `std::thread` and `std::packaged_task` are template classes. – John Apr 19 '22 at 04:29
  • 2
    @John `std::thread` is not a template, `std::thread(foo, 1)` is a template constructor `std::thread::thread`. – 273K Apr 19 '22 at 04:43
  • @273K I still have difficulty in understanding it. I think they both have ***template*** keyword in their declarations. – John Apr 20 '22 at 02:26
  • Where? [std::thread](https://en.cppreference.com/w/cpp/thread/thread) has no "template" in the whole page content. `std::thread(foo, 1)` is an unnamed object, `std::packaged_task` is a type. – 273K Apr 20 '22 at 03:40
  • @273K The [third constructor](https://en.cppreference.com/w/cpp/thread/thread/thread) is `template< class Function, class... Args > explicit thread( Function&& f, Args&&... args );`. – John Apr 20 '22 at 06:22
  • @John A class can have template member functions. `std::thread` is not a template, but it does have a template constructor. `std::packaged_task` is a class template (it also has template member functions) – Caleth Apr 20 '22 at 08:20
  • @Caleth I fully understand what you mean. Since the declaration is [`template explicit packaged_task( std::allocator_arg_t, const Allocator& a, F&& f );`](https://en.cppreference.com/w/cpp/thread/packaged_task/packaged_task), then why `std::packaged_task task{std::bind(foo, 1)}` works? ` I think `std::bind(foo, 1)` ` matches the third parameter of said declaration other than the first one. How do you think about it? – John Apr 20 '22 at 08:35
  • 1
    @John that's not the only constructor of packaged_task. your call uses `template explicit packaged_task( F&& f );` – Caleth Apr 20 '22 at 08:36

1 Answers1

2

std::packaged_task does not run a function/callable immediately, a function execution is deferred and parameters can be passed later to void operator()( ArgTypes... args );, therefore the possible constructor template< class Function, class... Args > explicit packaged_task( Function&& f, Args&&... args ); is not necessary.

However std::thread runs a function/callable immediately, thus parameters must be passed to the constructor template< class Function, class... Args > explicit thread( Function&& f, Args&&... args );. You may consider it like sugar to avoid persistently writing code like std::thread(bind(f, 1)); instead of convenient std::thread(f, 1);.

273K
  • 29,503
  • 10
  • 41
  • 64