为什么参数在构造 std::thread 时移动了两次

Why arguments moved twice when constructing std::thread

考虑这个程序,它本质上创建了 std::thread,它调用函数 func(),并以 arg 作为参数:

#include <thread>
#include <iostream>

struct foo {
   foo() = default;
   foo(const foo&) { std::cout << "copy ctor" << std::endl; }
   foo(foo&&) noexcept { std::cout << "move ctor" << std::endl; }
};

void func(foo){}

int main() {
   foo arg;
   std::thread th(func, arg);
   th.join();
}

我的输出是

copy ctor
move ctor
move ctor

据我了解,arg 在线程对象内部被复制,然后作为右值(移动)传递给 func()。所以,我希望一次复制构造一次移动构造

为什么会有二次移动构造?

您按值将参数传递给 func,这应该构成第二步。显然 std::thread 在调用 func 之前在内部再存储一次,这 AFAIK 在标准方面是绝对合法的。

So, I expect one copy construction and one move construction.

标准实际上并没有这样说。允许实现执行额外的内部移动构造。

虽然这样做可能效率较低。这是 https://gcc.gnu.org/PR69724 并且已针对即将发布的 GCC 10 版本进行修复。