2014-01-11 79 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 ::線程得到了同樣的錯誤。就像在實際函數的簽名中使用更具體的指針時給基類的指針一樣。

相關問題