std :: thread为什么对象被复制两次?

Krz*_*tof 6 c++ c++11

为什么在下面的示例代码中,对象被复制两次?根据线程类的文档构造函数将所有参数复制到线程本地存储,因此我们有理由进行第一次复制.第二个怎么样?

class A {
public:
    A() {cout << "[C]" << endl;}
    ~A() {cout << "[~D]" << endl;}
    A(A const& src) {cout << "[COPY]" << endl;}
    A& operator=(A const& src) {cout << "[=}" << endl; return *this;}

    void operator() () {cout << "#" << endl;}
};

void foo()
{
    A a;
    thread t{a};
    t.join();
}
Run Code Online (Sandbox Code Playgroud)

上面的输出:

[C]
[COPY]
[COPY]
[~D]
#
[~D]
[~D]
Run Code Online (Sandbox Code Playgroud)

编辑:是的,添加移动构造函数后:

A(A && src) {cout << "[MOVE]" << endl;}
Run Code Online (Sandbox Code Playgroud)

输出是这样的:

[C]
[COPY]
[MOVE]
[~D]
#
[~D]
[~D]
Run Code Online (Sandbox Code Playgroud)

小智 5

对于您想要移动或避免复制的任何内容,最好选择移动构造函数和std::move.

但为什么这不会自动发生在我身上呢?

C++ 中的移动是保守的。它通常只有在您明确写入时才会移动std::move()。这样做是因为移动语义如果扩展到非常明确的情况之外,可能会破坏旧代码。由于这个原因,自动移动通常仅限于非常谨慎的情况。

为了避免在这种情况下进行复制,您需要a通过使用进行切换std::move(a)(即使将其传递到std::thread)。它第一次创建副本的原因是因为 std::thread 无法保证在完成 std​​::thread 的构造之后该值将存在(并且您还没有显式地将其移入)。因此,它将做安全的事情并制作一个副本(不获取对您传入的内容的引用/指针并存储它:代码不知道您是否会使其保持活动状态)。

同时拥有移动构造函数和使用std::move将使编译器能够最大程度地、有效地移动您的结构。如果您使用 VC++(无论是否带有 CTP),则必须显式编写移动构造函数,否则 MSVC 将(甚至有时错误地)声明并使用复制构造函数。