2014-01-11 77 views
19

我的节目看起来像下面C++ 11 Thread类如何使用类成员函数

#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...>) 
     ^

回答

24

这不是引用包装的正确位置。一个简单的指针就足够了,不过,并达到所期望的结果:

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

+1为了得到正确的答案,而不是在GCC bugzilla上填写错误的bug,比如某种白痴。 – Casey

10

编辑:RETRACTION

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

The values of the bound argumentsv1, v2, ..., vN and their corresponding types V1, V2, ..., VN depend on the types TiD derived from the call to bind and the cv-qualifiers cv of the call wrapper g as follows:

  • if TiD is reference_wrapper<T> , the argument is tid.get() and its type Vi is T& ;
  • ...

所以的reference_wrapper<A>这个特定的用途是不通过std::thread支持,但支持std::bindstd::thread与其他/旧编译器中的std::bind行为相同的事实是缺陷,而不是4.8行GCC版本的行为。

我会在这里留下不正确的答案与这个解释,希望别人不会在未来犯同样的错误。

短(但不正确的)答案

这显然是在标准库中的缺陷包括在GCC 4.8。

龙(也是不正确的)答案:

的影响的代码是正确的编译std::thread构造

template <class F, class ...Args> 
explicit thread(F&& f, Args&&... args); 

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

The new thread of execution executes

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

with the calls to DECAY_COPY being evaluated in the constructing thread.

DECAY_COPY在30.2.6 [线索进行说明。decaycopy]/1:

In several places in this Clause the operation DECAY_COPY(x) is used. All such uses mean call the function decay_copy(x) and use the result, where decay_copy is defined as follows:

template <class T> typename decay<T>::type decay_copy(T&& v) 
{ return std::forward<T>(v); } 

在调用的OP std::thread t1(&A::foo, std::ref(a), 100);所有三个参数是右值是DECAY_COPY将复制到调用,在20.8.2 [func被描述其效果之前在新线程的环境中的对象。需要]/1:

Define INVOKE(f, t1, t2, ..., tN) as follows:

  • (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T ;
  • ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;
  • ...

对于在OP代码,f是指向A类的成员函数值&A::foot1是一个左值reference_wrapper<A>其存储的参考是指a,和t2是值为100int。 20.8.2/1的第二个项目符合。由于t1reference_wrapper*t1计算到存储的基准(每20.8.3.3/1),并在新的线程调用是有效

(a.*&A::foo)(100); 

所以,是的,标准的描述完全OP的行为预期。

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

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(); 
} 
+1

报告为[GCC bug 59768](http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59768)。 – Casey

+0

对不起,你是如何得出结论“'* t1'评估存储的参考”?我的20.8.3.3并没有这么说...... –

+0

@KerrekSB因为我错误地认为'* t1'“魔法地”调用了'operator T&'。 – Casey

7

GCC 4.8是在INVOKE的来定义正确,std::thread和其它部件必须不是在std::bind方面来实现。它们不得调用嵌套的绑定表达式,并且必须使用完美的转发来绑定参数(而不是像std::bind那样将它们转发为左值),并且您还发现它们不会拆开reference_wrapper对象。在GCC 4.8中,我引入了内部实现细节__bind_simple,供std::thread等使用,但没有完整的std::bind行为。

虽然从std::bind其他的差异是可取的,我觉得INVOKE操作还是应该支持reference_wrapper对象,所以我提出了缺陷报告,见LWG 2219

+1

类似'std :: thread t(&A :: foo,&a,std :: ref(b));'也是非法的(假设成员'A :: foo(Bar&)'类似的东西](http://ideone.com/x2O6eU)在gcc 4.8.2上编译,而且我想知道这是一个bug还是标准规定的行为。 – juanchopanza

+2

@juanchopanza,这是工作所必需的。是因为你调用成员的类对象不支持reference_wrappers,所以包装器对函数的参数工作正常 –

+0

完美,谢谢你的澄清 – juanchopanza

10

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

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

这似乎是一个“更清洁”的方法,把参数或对象或甚至函数暴露给线程,只要给线程所有想要的,这是一个'void(*)(void)'(好吧,这是描述它的最简单的方法)。 –

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; 
    } 
0

只是想补充一点,我只是给不兼容的参数为std ::绑定/的std ::线程得到了同样的错误。就像在实际函数的签名中使用更具体的指针时给基类的指针一样。

相关问题