我正在尝试使用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
答案 0 :(得分:5)
在您的情况下,正在复制obj
:
std::async
两次。async
对std::bind
的内部呼叫两次。void foo(obj a)
一次,因为它需要a
值。信不信由你,副本的数量实际上是reduced since VC10。
看到一个库(无论是标准库还是其他库)触发的数量比您在类型上所预期的要多一些,这并不罕见。通常情况下,您无法做太多事情。
人们通常会做两件事来阻止复制:
obj
为引用(或者在您的情况下,const ref,因为foo
不会修改obj
)。这需要使用std::ref
和异步。obj
定义move constructor。这不会阻止临时建造和销毁,但它会让你有机会优化这个过程。请注意,在一个仅保留一个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