Why the compiler complains if the the thread function delaration is changed to void thr(std::shared_ptr<Base>& p)
.Complie error:
gcc-10.1.0/include/c++/10.1.0/thread: In instantiation of 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(std::shared_ptr&); _Args = {std::shared_ptr&}; = void]': gcc-10.1.0/include/c++/10.1.0/thread:136:44: error: static assertion failed: std::thread arguments must be invocable after conversion to rvalues
136 | typename decay<_Args>::type...>::value,
Can someone explain me, step by step.
I would be grateful for any hint on this question.
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
struct Base
{
Base() { std::cout << " Base::Base()\n"; }
// Note: non-virtual destructor is OK here
~Base() { std::cout << " Base::~Base()\n"; }
};
struct Derived: public Base
{
Derived() { std::cout << " Derived::Derived()\n"; }
~Derived() { std::cout << " Derived::~Derived()\n"; }
};
void thr(std::shared_ptr<Base> p)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::shared_ptr<Base> lp = p; // thread-safe, even though the
// shared use_count is incremented
{
static std::mutex io_mutex;
std::lock_guard<std::mutex> lk(io_mutex);
std::cout << "local pointer in a thread:\n"
<< " lp.get() = " << lp.get()
<< ", lp.use_count() = " << lp.use_count() << '\n';
}
}
int main()
{
std::shared_ptr<Base> p = std::make_shared<Derived>();
std::cout << "Created a shared Derived (as a pointer to Base)\n"
<< " p.get() = " << p.get()
<< ", p.use_count() = " << p.use_count() << '\n';
std::thread t1(thr, p), t2(thr, p), t3(thr, p);
p.reset(); // release ownership from main
std::cout << "Shared ownership between 3 threads and released\n"
<< "ownership from main:\n"
<< " p.get() = " << p.get()
<< ", p.use_count() = " << p.use_count() << '\n';
t1.join(); t2.join(); t3.join();
std::cout << "after joining the threads\n" <<
" p.get() = " << p.get() << ", p.use_count() " <<p.use_count() << std::endl;
std::cout << "All threads completed, the last one deleted Derived\n";
}
The outputs:
Base::Base()
Derived::Derived()
Created a shared Derived (as a pointer to Base)
p.get() = 0x57be80, p.use_count() = 1
Shared ownership between 3 threads and released
ownership from main:
p.get() = 0, p.use_count() = 0
local pointer in a thread:
lp.get() = 0x57be80, lp.use_count() = 4
local pointer in a thread:
lp.get() = 0x57be80, lp.use_count() = 3
local pointer in a thread:
lp.get() = 0x57be80, lp.use_count() = 2
Derived::~Derived()
Base::~Base()
after joining the threads
p.get() = 0, p.use_count() 0
All threads completed, the last one deleted Derived
The arguments passed to the std::thread
constructor will be copied and then forwarded as rvalues to the function that runs in the new thread. So when you create a std::thread
like this:
std::thread t1(thr, p)
the argument p
will be copied, then forwarded as an rvalue. If the function thr
expects an lvalue reference then it can't be called with an rvalue.
The static assertion is telling that you that you can't call thr(shared_ptr<Base>&)
with an rvalue shared_ptr<Base>
. (Before I added the static assertion you just got a horrible template instantiation error from deep inside the guts of std::thread
, now the idea is that it tells you what's wrong in English).
The solution to passing a reference into the function is to use the std::ref
function to create a reference_wrapper
object:
std::thread t1(thr, std::ref(p))
This will create a std::reference_wrapper<std::shared_ptr<Base>>
which gets copied and forwarded to thr
as an rvalue, and then that rvalue can be converted to shared_ptr<Base>&
to initialize the parameter of the thr
function.
This is also clearly explained at https://en.cppreference.com/w/cpp/thread/thread/thread#Notes
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With