使用std::async调用成员函数时,传对象与对象指针为何效果不同?
std::async调用成员函数时传参导致拷贝的问题解答
问题场景
学习C并发编程时,遇到《C Concurrency in Action, 2nd Edition》4.2.1节代码清单4.7中的情况:使用std::async调用成员函数时,传对象指针不会拷贝原对象,直接传对象则会触发拷贝。
代码示例:
//Listing 4.7 Passing arguments to a function with std::async #include <string> #include <future> struct X { void foo(int,std::string const&); std::string bar(std::string const&); }; X x; auto f1=std::async(&X::foo,&x,42,"hello"); //Calls p->foo(42,"hello"), where p is &x auto f2=std::async(&X::bar,x,"goodbye"); //Calls tmpx.bar("goodbye"), where tmpx is a copy of x
疑问:两种传参方式为何产生不同结果?是否和右值有关?
解答
1. 成员函数的底层调用逻辑
非静态成员函数在编译时会被隐式添加第一个参数——指向类对象的this指针。也就是说,X::foo的实际签名等价于:
void foo(X* this, int, std::string const&);
调用成员函数时,必须为this指针绑定一个有效类实例的地址。
2. 传指针的情况(f1)
传递&x给std::async时,这个指针直接被绑定到成员函数的this参数上,最终执行的是(&x)->foo(42, "hello"),全程直接操作原对象x,没有拷贝行为。
3. 传对象的情况(f2)
直接传递x(左值对象)时,std::async的参数默认是按值传递的。它会先拷贝一份x到内部的临时存储(即书中的tmpx),然后用这个临时对象的地址作为this指针调用bar函数。这就是产生拷贝的核心原因——参数按值传递的规则,和右值没有直接关系。
如果传递的是右值(比如std::move(x)),std::async会用移动构造创建临时对象,而非拷贝构造,但本质还是会生成新对象,原对象的状态会被转移,同样不会直接操作原对象。
4. 如何避免拷贝操作
如果想直接操作原对象但不想传指针,可以用std::ref或std::cref包装对象,让std::async传递引用而非值:
auto f2 = std::async(&X::bar, std::ref(x), "goodbye"); // 直接操作原x,无拷贝
内容的提问来源于stack exchange,提问作者soyokaze
相关产品推荐
相关产品推荐

