I'm having this question because of the following example:
#include <utility>
#include <thread>
#include <iostream>
typedef struct foo{
foo() = default;
void operator()(int i) const {}
~foo(){std::cout<<"dtor\n";}
} foo;
typedef struct bar{
bar() = default;
bar(const bar&) {std::cout<<"copy\n";}
bar(bar&&) {std::cout<<"move\n";}
void operator()(const foo& f, int k) const {f(k);}
~bar(){std::cout<<"bar\n";}
} bar;
int main(){
foo f_1, f_2;
bar b_1, b_2;
int i(0), j(0);
while(i++!=2){
std::thread t(b_1, std::cref(f_1), i);
b_2(f_2, j);
t.join();
}
int dummy(0);
std::cin >> dummy;
}
which yields (both gcc and clang give the same result)
copy
move
bar
bar
copy
move
bar
bar
0
bar
bar
dtor
dtor
, where the 0 is user input.
So the dtor for bar--the argument for Function--is called twice after the tread finishes its job (for each iteration). What I don't understand, is why twice instead of just once(for making the copy)?
In addition, is it possible to avoid the copy, in case the functor itself holds non-copiable resources or is expensive to be copied?
Thank you!
UPDATE It's not necessarily twice as the original question asked, see Praetorian's answer below, where 3 dtor calls and 2 moves are involved.
You're passing an lvalue (b_1
) to the std::thread
constructor, so it'll copy that argument. I've modified your example so that it's easier to follow what's going on. Note that the while
condition has been changed so that it only executes once.
typedef struct foo{
foo() = default;
void operator()(int i) const {}
// ~foo(){std::cout<<"dtor\n";}
} foo;
typedef struct bar{
bar() {std::cout<<"bar " << this << '\n';}
bar(const bar&) {std::cout<<"bar copy " << this << '\n';}
bar(bar&&) {std::cout<<"bar move " << this << '\n';}
void operator()(const foo& f, int k) const {f(k);}
~bar() {std::cout<<"~bar " << this << '\n';}
} bar;
int main(){
foo f_1, f_2;
bar b_1, b_2;
int i(0), j(0);
while(i++!=1){
std::cout << "---- 1 ----\n";
std::thread t(b_1, std::cref(f_1), i);
std::cout << "---- 2 ----\n";
b_2(f_2, j);
t.join();
std::cout << "---- 3 ----\n";
}
}
On gcc this produces the output (annotations are mine)
bar 0x7fffbcc2156c // b_1 constructed
bar 0x7fffbcc2156d // b_2 constructed
---- 1 ----
bar copy 0x7fffbcc21580 // std::thread ctor makes copy of b_1
bar move 0x162a038 // that copy is moved by std::thread impl
~bar 0x7fffbcc21580 // moved from object is destructed
---- 2 ----
~bar 0x162a038 // bar object owned by thread instance is destroyed
---- 3 ----
~bar 0x7fffbcc2156d // b_2 is destroyed
~bar 0x7fffbcc2156c // b_1 is destroyed
The output from clang is identical.
There are a couple of different options if you want to avoid that copy. You can wrap the b_1
instance in std::reference_wrapper
before passing it to std::thread
.
std::thread t(std::cref(b_1), std::cref(f_1), i);
Or you can allow the std::thread
constructor to move the b_1
instance.
std::thread t(std::move(b_1), std::cref(f_1), i);
Live demo. In this case you'll incur the internal move construction performed by the std::thread
implementation.