If we have a class H with some operator() overloaded. How it is possible to create a thread from these member functions without instantiating an object from class H. Consider the following code
#include<iostream>
#include<thread>
class H {
public:
void operator()(){
printf("This is H(), I take no argument\n");
}
void operator()(int x){
printf("This is H(), I received %d \n",x);
}
};
int main(){
int param = 0xD;
//No object created
std::thread td_1 = std::thread(H());
std::thread td_2 = std::thread(H(),param);
td_1.join();
td_2.join();
//From an object
H h;
std::thread td_3 = std::thread(h);
std::thread td_4 = std::thread(h,param);
td_3.join();
td_4.join();
return 0;
}
produce the output :
This is H(), I take no argument
This is H(), I received 13
This is H(), I take no argument
This is H(), I received 13
The question is, how td_1 and td_2 called the member function operator() of class H without an object of class H?
how td_1 and td_2 called the member function operator() of class H without an object of class H?
td_1 and td_2 does create objects of type H. Those objects are temporaries. Next, those supplied function object(which are temporaries in this case) are moved/copied into the storage belonging to the newly created thread of execution and invoked from there.
You can confirm this by adding a default constructor and move constructor inside class H as shown below:
#include<iostream>
#include<thread>
class H {
public:
void operator()(){
printf("This is H(), I take no argument\n");
}
void operator()(int x){
printf("This is H(), I received %d \n",x);
}
//default constructor
H()
{
std::cout<<"default constructor called"<<std::endl;
}
//move constructor
H(H&&)
{
std::cout<<"move constructor called"<<std::endl;
}
};
int main(){
int param = 0xD;
std::thread td_1 = std::thread(H());
std::thread td_2 = std::thread(H(),param);
td_1.join();
td_2.join();
return 0;
}
The output of the above program is:
default constructor called
move constructor called
move constructor called
default constructor called
move constructor called
move constructor called
This is H(), I take no argument
This is H(), I received 13
Consider this function:
void f() {
printf("This is f(), I take no argument\n");
}
A thread that calls the function is constructed like std::thread(f). Code like std::thread(f()) is invalid, because the parameter of std::thread must be callable (in this case, a function object). If you call the function before passing it to the constructor, std::thread can no longer call it.
So you pass f to the constructor, and it later becomes f(), and the function is called.
Similar to passing the name of a function, you can pass an object to the constructor, and the thread later calls operator(). When you write std::thread(H()), you construct a temporary object. Because class H has operator(), this code is accepted.
In fact, std::thread(H{}) is also accepted. This shows that the parentheses refer to the constructor H::H(), rather than H::operator(). You did not write a constructor for the class, but the compiler creates a default constructor.
You could also use this code to construct a temporary object with H() and immediately call operator():
int main() {
H()(); // Valid: H() is callable
//f()(); // Invalid: f() is not callable
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With