c ++ 11 Thread类如何使用类成员函数

时间:2014-01-11 06:05:29

标签: c++ c++11

我的程序如下所示

#include <iostream>
#include <thread>

class A {
public:
    void foo(int n ) { std::cout << n << std::endl; }
};

int main()
{
    A a;

    std::thread t1(&A::foo, std::ref(a), 100);

    t1.join();
    return 0;
}

当我使用以下命令编译它时,我得到错误

g++ -o main main.cc -lpthread -std=c++11

错误:

In file included from /usr/local/include/c++/4.8.2/thread:39:0,
                  from check.cc:2:
/usr/local/include/c++/4.8.2/functional: In instantiation of ‘struct std::_Bind_simple<std::_Mem_fn<void (A::*)(int)>(std::reference_wrapper<A>, int)>’:
/usr/local/include/c++/4.8.2/thread:137:47:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (A::*)(int); _Args = {std::reference_wrapper<A>, int}]’
check.cc:13:42:   required from here
/usr/local/include/c++/4.8.2/functional:1697:61: error:no type named ‘type’ in ‘class std::result_of<std::_Mem_fn<void (A::*)(int)>(std::reference_wrapper<A>, int)>’
        typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                              ^
/usr/local/include/c++/4.8.2/functional:1727:9: error:no type named ‘type’ in ‘class std::result_of<std::_Mem_fn<void (A::*)(int)>(std::reference_wrapper<A>, int)>’
          _M_invoke(_Index_tuple<_Indices...>)
          ^

6 个答案:

答案 0 :(得分:25)

这不是参考包装器的正确位置。但是,一个简单的指针就足够了,并且达到了预期的结果:

std::thread t1(&A::foo, &a, 100);

答案 1 :(得分:11)

编辑:撤回

Kerrek在这里是正确的:我错误地认为std::thread构造函数和std::bind是设计相同的接口。但是,在[func.bind.bind] / 10中仅为reference_wrapper<A>指定了从A&std::bind的参数自动转换:

  

绑定参数 v1, v2, ..., vN及其对应类型V1, V2, ..., VN的值取决于从TiD调用派生的类型bind和调用包装器g cv - 限定符 cv 如下:

     
      
  • 如果TiDreference_wrapper<T>,则参数为tid.get(),其类型ViT&;
  •   
  • ...
  •   

reference_wrapper<A>std::thread的这种特殊使用支持std::bind,但std::thread支持 。在其他/较旧的编译器中std::bind在此实例中与std::thread的行为相同的事实是错误,而不是4.8行GCC版本的行为。

我会在这里留下错误的答案,并希望其他人在将来不会犯同样的错误。

简短(但不正确)回答

这显然是GCC 4.8附带的标准库中的一个错误。代码通过以下方式正确编译:

长(也是不正确)回答:

template <class F, class ...Args> explicit thread(F&& f, Args&&... args); 构造函数的效果

INVOKE(DECAY_COPY(std::forward<F>(f)),
       DECAY_COPY(std::forward<Args>(args))...)

详见C ++ 11 30.3.1.2 [thread.thread.constr] / 4:

  

新的执行线程执行

DECAY_COPY
     

在构造线程中对DECAY_COPY的调用进行评估。

DECAY_COPY(x)在30.2.6 [thread.decaycopy] / 1:

中描述
  

在本条款的几个地方使用了操作decay_copy(x)。所有这些用法都意味着调用函数decay_copy并使用结果,其中template <class T> typename decay<T>::type decay_copy(T&& v) { return std::forward<T>(v); } 定义如下:

std::thread t1(&A::foo, std::ref(a), 100);

在OP DECAY_COPY的调用中,所有三个参数都是INVOKE(f, t1, t2, ..., tN)将在调用之前复制到新线程环境中的对象的rvalues,其效果在20.8.2中描述[func.require] / 1:

  

如下定义(t1.*f)(t2, ..., tN)

     
      
  • fT是指向类t1的成员函数的指针时,TT类型的对象或对{类型为T的对象或对从((*t1).*f)(t2, ..., tN);
  • 派生的类型的对象的引用   
  • fT是指向类t1的成员函数的指针时,f不是上一项中描述的类型之一;
  •   
  • ...
  •   

对于OP中的代码,A是指向类&A::foo的成员函数的指针,其值为t1reference_wrapper<A>是左值a,存储的引用是指t2,而int100,其值为t1。适用20.8.2 / 1的第二个子弹。由于reference_wrapper*t1(a.*&A::foo)(100); 计算存储的引用(按照20.8.3.3/1),并且新线程中的调用实际上是

class A {
public:
    void foo(int n) { std::cout << n << std::endl; }
};

int main()
{
    A a;
    auto foo = std::bind(&A::foo, std::ref(a), 100);
    foo();
}

是的,该标准完全按照预期描述了OP的行为。

编辑:奇怪的是,GCC 4.8 correctly compiles the very similar example

{{1}}

答案 2 :(得分:10)

关于你的问题标题,我会使用lambda进行线程构建。有或没有引用,通过调用成员函数或绑定参数。

 std::thread t1([&] { a.foo(100); });

答案 3 :(得分:7)

GCC 4.8是正确的,std::thread和根据 INVOKE 定义的其他组件不得以std::bind的形式实现。它们不能调用嵌套的绑定表达式,并且必须对绑定参数使用完美转发(而不是像std::bind那样将它们转发为左值),另外如您发现它们不会解包reference_wrapper个对象。在GCC 4.8中,我介绍了一个内部实现细节__bind_simple,供std::thread等使用,但没有完整的std::bind行为。

虽然与std::bind存在其他差异,但我认为 INVOKE 操作仍应支持reference_wrapper个对象,因此我提交了一份缺陷报告,请参阅{{3} }。

答案 4 :(得分:0)

好的问题是ref(obj)向对象返回引用(别名)而不是指针(地址)!使用线程我们需要指针而不是引用!请参阅下面一个方便的程序,以使用带线程的函数指针:

    #include <iostream>
    #include "vector"
    #include "string"
    #include "thread"
    #include "atomic"
    #include "functional"

    #include "stdlib.h"
    #include "stdio.h"
    #include "string.h"
    #include "assert.h"

    using namespace std;
    //__________________________Global variables_________________________________________________

    atomic<int> var(0);

    //__________________________class____________________________________________________________

    class C
    {
    public:

        C()
        {}

        static void addition (int a, int b)
        {
            for(int i= 0; i< a+b; i++)
                var++;
        }

        void subtraction (int a, int b)
        {
            for(int i= 0; i< a+b; i++)
                var--;
        }
    };

    class D : std::atomic<int>
    {
    public:
        D() : std::atomic<int>(0)
        {}

        void increase_member (int n)
        {
            for (int i=0; i<n; ++i)
                fetch_add(1);
        }

        int get_atomic_val()
        {
            return this->load();
        }
    };

    //________________________________functions________________________________________________

    void non_member_add (int a, int b)
    {
        for(int i= 0; i< a+b; i++)
            var++;
    }

    //__________________________________main____________________________________________________

    int main ()
    {
        int a=1, b=5;

    // (I)...........................................static public member function (with no inheritance).........................................

        void (* add_member_func_ptr)(int,int) = C::addition;            // pointer to a static public member function

        //defining thread pool for ststic public member_add_ptr

        vector<thread> thread_pool;

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(add_member_func_ptr,a,b));
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"static public member function (with no inheritance)\t"<<var<<endl;

        //defining thread pool for ststic public member function

        var=0;

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(C::addition,a,b));             //void (* add_member_func_ptr)(int,int) is equal to C::addition
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"static public member function (with no inheritance)\t"<<var<<endl;

    // (II)..............................................non-static public member function (with no inheritance)...................................

        C bar;

        void (C::* sub_member_func_ptr)(int,int) = & C::subtraction;            // pointer to a non-static public member function

        var=0;

        //defining thread pool for non-ststic public member function

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(sub_member_func_ptr,bar,a,b));
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"non-static public member function (with no inheritance)\t"<<var<<endl;

        var=0;

        //defining thread pool for non-ststic public member function

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(&C::subtraction,bar,a,b));         //void (C::* sub_member_func_ptr)(int,int) equals & C::subtraction;
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"non-static public member function (with no inheritance)\t"<<var<<endl;


    // (III)................................................non-member function .................................................

        void (* non_member_add_ptr)(int,int) = non_member_add;              //pointer to a non-member function

        var=0;

        //defining thread pool for non_member_add

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(non_member_add,a,b));
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"non-member function\t"<<var<<endl<<endl;

    // (IV)...........................................non-static public member function (with inheritance).........................

        D foo;

        void (D::* member_func_ptr) (int) = & D::increase_member;                  //pointer to a non-static public member function of a derived class

        //defining thread pool for non-ststic public member function of a derived class

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            thread_pool.push_back(thread(member_func_ptr,&foo,10));                 //use &foo because this is derived class!
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"non-static public member function (with inheritance)\t"<<foo.get_atomic_val()<<endl;

        //defining thread pool for non-ststic public member function

        D poo;

        thread_pool.clear();

        for (int i=0; i<5; i++)
        {
            reference_wrapper<D> poo_ref= ref(poo);

            D& poo_ref_= poo_ref.get();             //ref(obj) returns a reference (alias) to an object not a pointer(address)!

            D* d_ptr= &poo;                         //to work with thread we need pointers not references!


            thread_pool.push_back(thread(&D::increase_member, d_ptr,10));             //void (D::* member_func_ptr) (int) equals & D::increase_member;
        }

        for(thread& thr: thread_pool)
            thr.join();

        cout<<"non-static public member function (with inheritance)\t"<<poo.get_atomic_val()<<endl<<endl;


        return 0;
    }

答案 5 :(得分:0)

只是想通过向std :: bind / std :: thread提供不兼容的参数来添加我得到了同样的错误。就像当一个更具体的指针在实际函数的签名中时,给出一个指向基类的指针。