I am reading open source code of thread_pool
And it shows
// add new work item to the pool
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>
{
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared< std::packaged_task<return_type()> >(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex);
// don't allow enqueueing after stopping the pool
if(stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
tasks.emplace([task](){ (*task)(); });
}
condition.notify_one();
return res;
}
As far as I know, std::packaged_task
is a only movable object, so could a shared_ptr of task
be made of std::packaged_task
?Because std::packaged_task
could never be shared
.
But in an oppisite way, in sentence tasks.emplace([task](){ (*task)(); });
, task
is value-copied into lambda, so does it also mean the count in shared_ptr of task
will be added into 2?Which means std::packaged_task
is actually be shared
into 2 copies.
So can a only-movable object be made into a shared_ptr?Are there any wrong place in my above thoughts?
Smart pointers don't even need the element type to be movable. They never attempt to make any copies in any form. All make_shared
does is construct an object once and then shared_ptr
just pass around a pointer to that single object.
Copying a shared_ptr
is just like copying a raw pointer (plus the reference counting of course). It doesn't make a copy of the pointed-to object.
You wouldn't need the type to be copyable or movable either if all you did manually was to use new
to create it once and then work through (copies of) the resulting raw pointer.