std :: async和对象副本

时间:2013-02-16 15:55:25

标签: 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;
}

结果是:

new obj
delete obj
delete obj
delete obj
5
delete obj
delete obj
delete obj

然后我尝试按void foo(obj a)更改void foo(obj &a)

new obj
delete obj
delete obj
delete obj
5
delete obj
delete obj

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

修改

我正在使用VS2012

2 个答案:

答案 0 :(得分:5)

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

  1. 拨打std::async两次。
  2. asyncstd::bind的内部呼叫两次。
  3. 通过调用void foo(obj a)一次,因为它需要a值。
  4. 信不信由你,副本的数量实际上是reduced since VC10

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

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

    1. obj为引用(或者在您的情况下,const ref,因为foo不会修改obj)。这需要使用std::ref和异步。
    2. obj定义move constructor。这不会阻止临时建造和销毁,但它会让你有机会优化这个过程。
    3. 请注意,在一个仅保留一个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;
      }
      

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

      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;
          }
      
          /* ... */
      };
      

答案 1 :(得分:2)

在绑定阶段正在复制

a。要避免a的多个副本,请使用move constructor语义:

move ctor添加到obj

class obj {
public:
    ...
    obj(obj&& other) {
        cout << "move obj" << endl;
        val = std::move(other.val);
    }
};

主要:

    obj a(5);
    auto future = async(foo, std::move(a));
    ...

这样,仍然会创建5个obj实例,但由于异步支持movable个对象,所以同一个副本将从一个实例移动到另一个实例(对于重对象,这将比复制对象重要) 。所以现在输出应该是:

new obj
move obj
move obj
move obj
move obj
delete obj
delete obj
delete obj
5
delete obj
delete obj