std :: async和对象副本

use*_*743 5 c++ c++11

我正在尝试std::async并最终得到一个看起来像这样的代码:

class obj {
public:
    int val;

    obj(int a) : val(a) {
        cout << "new obj" << endl;
    }
    ~obj() {
        cout << "delete obj" << endl;
    }
};


void foo(obj a) {

    this_thread::sleep_for(chrono::milliseconds(500));
    cout << a.val << endl;
}

int main(int argc, int **args) {

    obj a(5);
    auto future = async(foo, a);
    future.wait();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果是:

new obj
delete obj
delete obj
delete obj
5
delete obj
delete obj
delete obj
Run Code Online (Sandbox Code Playgroud)

然后我试图改变void foo(obj a)void foo(obj &a):

new obj
delete obj
delete obj
delete obj
5
delete obj
delete obj
Run Code Online (Sandbox Code Playgroud)

为什么要为这个简单的代码制作5个我的对象副本?我不得不承认,我真的很困惑.有人会解释这个吗?

编辑

我正在使用VS2012

Sea*_*ine 5

在您的情况下,obj正在被复制:

  1. 两次致电std::async.
  2. 两次async内部呼唤std::bind.
  3. 一旦通过调用,void foo(obj a)因为它需要a按值.

不管你信不信,自VC10以来,副本数量实际上已经减少了.

看到一个库(无论是标准库还是其他库)触发的数量比您在类型上预期的要多一些,这并不罕见.而且通常情况下,您无法做太多.

人们通常会做两件事来防止复制:

  1. 就拿obj引用(或在您的情况下,const的裁判因为foo不修改obj).这将需要使用std::refasync.
  2. 定义转移构造obj.这不会阻止临时建造和销毁,但它会让你有机会优化这个过程.

请注意,在一个仅保留一个对象的裸示例中int,复制实际上可能更快,而不是通过引用移动或传递.


obj通过引用传递给的示例async:

void foo(const obj& a) {
    this_thread::sleep_for(chrono::milliseconds(500));
    cout << a.val << endl;
}

int main(int argc, int **args) {
    obj a(5);
    auto future = async(foo, std::cref(a));
    future.wait();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

定义移动构造函数的示例:

class obj
{
public:
    /* ... */

    obj(obj&& a) : val(move(a.val)) {
        // It is good practice to 0 out the moved object to catch use-after-move bugs sooner.
        a.val = 0;
    }

    /* ... */
};
Run Code Online (Sandbox Code Playgroud)