为什么编译器抱怨 std::thread 参数在转换为右值后必须是可调用的?

sun*_*369 3 c++ shared-ptr

如果线程函数 delaration 更改为void thr(std::shared_ptr<Base>& p).Complie 错误,为什么编译器会抱怨:

gcc-10.1.0/include/c++/10.1.0/thread: 在 '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: 错误: 静态断言失败: std::thread 参数在转换为右值后必须是可调用的

136 | 类型名称衰减<_Args>::type...>::value,

谁能给我解释一下,一步一步。

对于这个问题的任何提示,我将不胜感激。

#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";
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

Jon*_*ely 11

传递给std::thread构造函数的参数将被复制,然后作为右值转发给在新线程中运行的函数。所以当你创建一个std::thread这样的:

std::thread t1(thr, p)
Run Code Online (Sandbox Code Playgroud)

参数p将被复制,然后作为右值转发。如果函数thr需要左值引用,则不能用右值调用它。

静态断言告诉您不能thr(shared_ptr<Base>&)使用 rvalue 进行调用shared_ptr<Base>。(在我添加静态断言之前,你刚刚从 的内部深处得到了一个可怕的模板实例化错误std::thread,现在的想法是它告诉你英语有什么问题)。

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))
Run Code Online (Sandbox Code Playgroud)

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