2016-05-14 100 views
4

我有一個很奇怪的問題。爲了簡單起見,可以說,我想有一個函數,它接受2個相同的函數聲明作爲參數Lambda作爲模板函數

template<typename Func> 
void foo(Func a, Func b) 
{ 
    std::cout << "good"; 
} 

要嘗試的事情了我帶着putchar從cstdio,並創建了一個相同的功能相匹配的putchar

int myPutcharFunc(int) 
{ 
    return 0; 
} 

int main() 
{ 
    auto myPutcharLambda = [](int) -> int 
    { 
     return 0; 
    }; 

    foo(putchar, myPutcharFunc); // okay 
    foo(putchar, myPutcharLambda); //deduced conflicting types for parameter 'Func' ('int (__attribute__((__cdecl__)) *)(int)' and 'main()::<lambda(int)>') 
} 

現在,lambda不想編譯(關鍵是我想用lambda捕獲)。

因此,讓我們添加模板專業化,因爲程序員比機器更聰明,對吧? :)

template<typename Func> 
void foo(Func a, Func b) 
{ 
    std::cout << "good"; 
} 

template<> 
void foo(int(*)(int), int(*)(int)) 
{ 
    std::cout << "good"; 
} 

沒有運氣,同樣的錯誤 - 爲什麼? 但由於某些原因,當我註釋掉模板特殊化:

//template<> 
void foo(int(*)(int), int(*)(int)) 
{ 
    std::cout << "good"; 
} 

代碼編譯。我顯然不想爲每一組函數的參數重載foo - 這就是模板的用途。每個步驟都使用msvC++和g ++進行測試。我究竟做錯了什麼?

回答

2

每個拉姆達是不同類型的,所以你需要有兩個不同的模板參數,以獲得他們

template<typename FuncA, typename FuncB> 
void foo(FuncA a, FuncB b) 

類型推斷模板類型(見註釋修正)時,不腐爛。所以一個lambda仍然是一個lambda,並不會衰減到一個函數指針。同樣的原因,字符串文字被推斷爲char[N]而不是const char *

隨着你的第二個例子使用專業化,它不想使用你的專業化,因爲lambda不是一個函數指針。您可以將Lambda投射到函數指針並使其工作:https://godbolt.org/g/ISgPci技巧您可以在這裏執行的是say + my_lambda,因爲+是針對指針定義的,因此它會強制非捕獲lambda成爲函數指針。

+0

* 「演繹模板類型時,類型不衰」。 *「相同的原因,字符串文字被推斷爲char [N]而不是const char *」*,原始字符串文字被推斷爲const char * –

+0

@PiotrSkotnicki它是什麼時候作爲char數組出現呢?我只是在其他一些問題上讀到這個。 – xaxxon

+0

功能參數爲參考類型時 –

2

兩種可能性。

:只要把+在lambda前面:

foo(putchar, +myPutcharLambda); 

即工作,因爲一元+期望的整數樣值,諸如指針。因此,lambda轉換爲函數指針。

最終,(非捕獲)lambda與函數指針不具有相同的類型,即使它願意轉換爲函數指針。

編譯器應該如何知道允許哪些轉換生成相同類型的兩個對象?

:還有另一種選擇,利用的事實?:是願意做一些轉換,converting one type to another in some circumstances

template<typename Func1, typename Func2> 
void foo2(Func1 a, Func2 b) 
{ 
    using common_type = decltype(true?a:b); // or 'false', it doesn't matter 
    foo<common_type>(a,b); 
} 
+1

選項1特別調皮。 :) – erip

1

一個lambda都有自己的類型,可以衰減到一個函數指針,但不是一個模板函數匹配的情況下,它會爲真正的功能,你發現,因爲隱式轉換。

在匹配模板的情況下,您需要消除歧義並使用所需的類型顯式實例化foo,或將lambda轉換爲函數指針。 *當然,他們這樣做

foo<decltype(putchar)>(putchar, myPutcharLambda); 

foo(putchar, +myPutcharLambda);