在 C++ 中,std::thread 如何在不创建对象的情况下调用成员函数?
In C++, How a std::thread can call a member function without creating an object?
如果我们有一个 class H
有一些 operator()
超载。如何在不从 class H
实例化对象的情况下从这些成员函数创建线程。考虑以下代码
#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;
}
产生输出:
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
问题是,td_1和td_2如何在没有class对象的情况下调用classH
的成员函数operator()
H
?
how td_1 and td_2 called the member function operator() of class H without an object of class H?
td_1
和 td_2
确实创建了 H
类型的对象。这些对象是临时的。接下来,那些提供的函数对象(在这种情况下是临时的)被moved/copied放入属于新创建的执行线程的存储中并从那里调用。
您可以通过在 class H
中添加 默认构造函数 和 移动构造函数 来确认这一点,如图所示下面:
#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;
}
上述程序的output为:
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
语法 H()
在此上下文中创建类型 H
的临时对象。临时对象被传递给 std::thread
构造函数。
现在,如果线程在那个临时文件上调用 operator()
,那将是一个问题,因为临时文件只会存在到行尾 std::thread td_1 = std::thread(H());
并且线程函数可能之后执行。
但是,线程实际上并没有使用那个临时的。取而代之的是,为线程创建了一个(衰减的)参数类型的新对象,copied/moved 来自您提供给构造函数的临时对象。此 H
对象一直存在,直到线程退出并调用此对象 operator()
。
考虑这个函数:
void f() {
printf("This is f(), I take no argument\n");
}
调用函数的线程的构造类似于std::thread(f)
。 std::thread(f())
这样的代码是无效的,因为std::thread
的参数必须是callable (in this case, a function object)。如果在将函数传递给构造函数之前调用该函数,std::thread
将无法再调用它。
所以你把f
传给构造函数,后来就变成了f()
,函数就被调用了。
类似于传递函数名,可以将对象传递给构造函数,线程稍后调用operator()
。当你写 std::thread(H())
时,你构造了一个临时对象。因为classH
有operator()
,所以接受这个代码
其实std::thread(H{})
也是可以接受的。这表明括号是指构造函数H::H()
,而不是H::operator()
。您没有为 class 编写构造函数,但编译器会创建默认构造函数。
您也可以使用此代码构造一个带有 H()
的临时对象并立即调用 operator()
:
int main() {
H()(); // Valid: H() is callable
//f()(); // Invalid: f() is not callable
}
如果我们有一个 class H
有一些 operator()
超载。如何在不从 class H
实例化对象的情况下从这些成员函数创建线程。考虑以下代码
#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;
}
产生输出:
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
问题是,td_1和td_2如何在没有class对象的情况下调用classH
的成员函数operator()
H
?
how td_1 and td_2 called the member function operator() of class H without an object of class H?
td_1
和 td_2
确实创建了 H
类型的对象。这些对象是临时的。接下来,那些提供的函数对象(在这种情况下是临时的)被moved/copied放入属于新创建的执行线程的存储中并从那里调用。
您可以通过在 class H
中添加 默认构造函数 和 移动构造函数 来确认这一点,如图所示下面:
#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;
}
上述程序的output为:
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
语法 H()
在此上下文中创建类型 H
的临时对象。临时对象被传递给 std::thread
构造函数。
现在,如果线程在那个临时文件上调用 operator()
,那将是一个问题,因为临时文件只会存在到行尾 std::thread td_1 = std::thread(H());
并且线程函数可能之后执行。
但是,线程实际上并没有使用那个临时的。取而代之的是,为线程创建了一个(衰减的)参数类型的新对象,copied/moved 来自您提供给构造函数的临时对象。此 H
对象一直存在,直到线程退出并调用此对象 operator()
。
考虑这个函数:
void f() {
printf("This is f(), I take no argument\n");
}
调用函数的线程的构造类似于std::thread(f)
。 std::thread(f())
这样的代码是无效的,因为std::thread
的参数必须是callable (in this case, a function object)。如果在将函数传递给构造函数之前调用该函数,std::thread
将无法再调用它。
所以你把f
传给构造函数,后来就变成了f()
,函数就被调用了。
类似于传递函数名,可以将对象传递给构造函数,线程稍后调用operator()
。当你写 std::thread(H())
时,你构造了一个临时对象。因为classH
有operator()
,所以接受这个代码
其实std::thread(H{})
也是可以接受的。这表明括号是指构造函数H::H()
,而不是H::operator()
。您没有为 class 编写构造函数,但编译器会创建默认构造函数。
您也可以使用此代码构造一个带有 H()
的临时对象并立即调用 operator()
:
int main() {
H()(); // Valid: H() is callable
//f()(); // Invalid: f() is not callable
}