2012-05-20 64 views
3

我對gcc 4.5.2有一個有趣的問題。下面的代碼啓動std :: thread時無法綁定(函數指針)左值爲(函數指針)右值?

#include<thread> 
#include<iostream> 

using std::cout; 
void foo(int a){ 
    cout<<a; 
} 

template <typename T> 
void goo(void (*fn)(T),T c){ 
    fn(c); 
} 

int main(void) 
{ 
    std::thread TH; 
    void (*ptr)(int)=foo; 
    TH= std::thread(goo<int>,ptr,1); 
    TH.join(); 

    return 0; 
} 

..不會error: cannot bind ‘void(void (*)(int), int)’ lvalue to ‘void (&&)(void (*)(int), int)’其次是第二個錯誤initializing argument 1 of ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void(void (*)(int), int), _Args = {void (*&)(int), int}]’

但是在GCC 4.5.2編譯,當template被刪除這段代碼確實編譯,它也確實與編譯gcc 4.7.0(已有template)。

即使這是一個編譯器問題,有人能解釋一下這樣的錯誤是什麼意思嗎?我會很高興找到一種方法來進行綁定(即使這是在gcc 4.7中自動執行的)。

+0

爲什麼要支持GCC 4.5.2?如果你一般都在做這種事情,那麼這個程序的其他部分是否仍然可能會違反它的限制呢? – Potatoswatter

+0

我正在編寫CUDA/C++代碼,可以通過預處理器標誌進行調整;沒有更多的C++ 11被發現在代碼中的任何地方('nvcc'不支持它..所以我堅持4.5.2進行編譯,不喜歡改變編譯器只是因爲這個有趣的問題出現在CPU的情況下... –

+1

在gcc上工作4.7.0 – pmr

回答

5

你在兩個錯誤絆倒了。

海灣合作委員會的第一個錯誤是,在「完美轉發」 - 價值類別扣除期間,它認爲goo<int>是一個右值。但goo<int>實際上是一個左值。因此,不應將_Callable推斷爲void(void (*)(int), int),而應推斷爲void(&)(void (*)(int), int)。然後引用崩潰會產生左值引用作爲參數類型,而不是void(&&)(void (*)(int), int)就像它錯誤地用你的GCC版本。

在參數的實際初始化期間,它也錯誤地拒絕了函數左值對右值引用參數的初始化(我不記得GCC4.5發佈時工作草案的狀態 - 但可能是草稿那時候它已經不合格了)。對於函數類型表達式,Standard允許使用函數類型的左值初始化函數類型的右值引用。

解決模板ID的功能左值是相當令人費解,所以它不會讓我感到吃驚GCC聽錯了(見http://llvm.org/bugs/show_bug.cgi?id=7505http://llvm.org/bugs/show_bug.cgi?id=7505爲多少規則看似簡單的東西互動的兩個例子)。

+0

謝謝! (順便說一句:第二條鏈接=第一條鏈接)。在開始轉換/扣除之前,用'C++ 11'的術語來檢查編譯器對對象的看法是什麼?我現在使用'A = std :: move(&goo );'('A':完全不兼容的類型),以獲得更詳細的錯誤消息。另外 - 是否有辦法顯示實際執行的轉換類型,以便我們不需要猜測? [從'std :: move'我推斷,gcc4.5.2/3真的有問題,而gcc4.6和gcc4.7似乎完全按照你所說的應該做的。] –

+0

我不明白你的評論。如果你想知道gcc內部究竟發生了什麼,你別無選擇,只能看看它的代碼。 –

+0

是的,但這將是「昂貴」的解決方案。通過嘗試(糟糕的)轉換故意生成錯誤,使我(某些人)能夠快速回答編譯器的想法,例如'&goo '是。但是我需要知道,錯誤消息反映了已執行的一些嘗試轉換之後的狀態,例如「&goo 」,而不是它。但是 - 正如你所說 - 只是知道gcc使用的算法會更好。 –

4

左值是一個表達式,表示可以取得其地址的對象。例如,對基元類型的賦值表達式的左側必須是左值:int i; i = 3;正常,而5 = 3則不正確。不同的是,&i是好的,但&5不是。

goo<int>是函數類型的左值,但函數類型的表達式在C和C++中基本上是無用的。通過獲取地址,它們總是被轉換爲函數指針。由此產生的指針不是左值,因爲這將取得地址的地址。

G ++中的錯誤發生在隱式地址。顯然,轉換髮生在模板扣除之前,當您通過非模板goo但在通過goo<int>後。你需要它發生得更早,所以構造函數不會嘗試接收引用。

一元&操作足以強制轉換:

TH= std::thread(&goo<int>,ptr,1); 

http://ideone.com/mI05j

+0

是的,'''也夠我的原始問題。感謝關於如何處理函數指針的信息! –

+1

C++編譯器不會將函數左值僅轉換爲指針(C僅用於)。在這兩種情況下,GCC都不應該執行指針轉換的功能。我不認爲它確實如此。 –

+0

@ JohannesSchaub-litb吧? §4.3:「函數類型T的左值可以轉換爲類型爲」指向T的指針「,結果是指向函數的指針。」與C相同。 – Potatoswatter