在 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_1td_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()) 时,你构造了一个临时对象。因为classHoperator(),所以接受这个代码

其实std::thread(H{})也是可以接受的。这表明括号是指构造函数H::H(),而不是H::operator()。您没有为 class 编写构造函数,但编译器会创建默认构造函数。

您也可以使用此代码构造一个带有 H() 的临时对象并立即调用 operator():

int main() {
    H()(); // Valid: H() is callable
    //f()(); // Invalid: f() is not callable
}