Std :: async and a copy of the object

I experimented with std::async and got code that looks like this:

 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; } 

and the result:

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

Then I tried changing void foo(obj a) to void foo(obj &a) :

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

Why were 5 instances of my object created for this simple code? I must admit, I'm really confused. Would anyone like to explain this?

Edit

I am using VS2012

+4
c ++ c ++ 11
source share
2 answers

In your case, obj copied:

  • Twice on a call to std::async .
  • Twice on async internal call to std::bind .
  • Once upon a call void foo(obj a) , since it takes the value a by value.

Believe it or not, the number of copies has actually been reduced from the VC10 .

It is not unusual for a library (whether it be a standard library or another) to run multiple copies than you would expect from your types. And, as a rule, not much can be done in this.

There are two things that people usually do to prevent copying:

  • Take obj by reference (or in your case, const ref, since foo does not change obj ). This will require using std::ref with asynchronism.
  • Define a move constructor for obj . This will not prevent the creation and destruction of temporary objects, but it will give you the opportunity to optimize the process a bit.

Please note that in your bare example of an object that contains only one int , it is more likely to copy than move or pass by reference.


Example for passing obj by reference in 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; } 

An example of the definition of a move constructor

 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; } /* ... */ }; 
+5
source share

a copied during the binding phase. To avoid multiple copies of a, use the semantics of move constructor :

Add move ctor to obj :

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

Mostly:

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

Thus, 5 obj instances will still be created, but since async supports movable objects, the same copy will be moved from one instance to another (for heavy objects this will be significant for copying an object). So, now the output should be:

 new obj move obj move obj move obj move obj delete obj delete obj delete obj 5 delete obj delete obj 
+1
source share

All Articles