2016-06-08 71 views
33

我想能夠獲得一個函數指針指向一個lambda在C++中。獲取函數指針給lambda?

我可以這樣做:

int (*c)(int) = [](int i) { return i; }; 

,當然,下面的工作 - 即使它不是建立一個函數指針。

auto a = [](int i) { return i; }; 

但以下幾點:

auto *b = [](int i) { return i; }; 

給出了這樣的錯誤在GCC:

main.cpp: In function 'int main()': 
main.cpp:13:37: error: unable to deduce 'auto*' from '<lambda closure object>main()::<lambda(int)>{}' 
    auto *b = [](int i) { return i; }; 
            ^
main.cpp:13:37: note: mismatched types 'auto*' and 'main()::<lambda(int)>' 

似乎武斷了lambda可轉換爲沒有問題的函數指針,但是編譯器無法推斷函數類型並使用auto *創建指向它的指針。特別是當它可以隱式轉換unique, lambda type一個函數指針:

int (*g)(int) = a; 

我在http://coliru.stacked-crooked.com/a/2cbd62c8179dc61b包含上面的例子創建一個小的測試牀。在C++ 11和C++ 14下,這種行爲是相同的。

+4

規則'auto'推論都是非常相似的模板扣除。這裏你有扣除和轉換:你只能有一個。 – milleniumbug

+1

我想將問題標題更改爲「爲什麼auto *不會從lambda推導出函數指針類型?」爲了使這成爲一個具體的問題。這似乎是令你困惑的事情。但是你會更喜歡一些更像是「如何從lambda函數獲取函數指針而不寫入類型?」 –

+0

第一個標題聽起來不錯。 – oconnor0

回答

40

這種失敗:

auto *b = [](int i) { return i; }; 

因爲拉姆達是不是指針。 auto不允許轉換。儘管lambda 可以轉換爲指針,但這不會爲您完成 - 您必須自己動手。是否帶投:

auto *c = static_cast<int(*)(int)>([](int i){return i;}); 

或者與some sorcery

auto *d = +[](int i) { return i; }; 
+6

這是做什麼的從來沒有見過這種'+'語法 –

+0

@ Jaa-c添加了一個鏈接 – Barry

+4

謝謝 - 現在我將不得不使用它至少一次審稿人會恨我 –

6

尤其是當它獨特的,λ型隱式轉換函數指針:

但不能將其轉換爲「一個函數指針」。它只能將其轉換爲指向特定函數簽名的指針。這將失敗:

int (*h)(float) = a; 

爲什麼會失敗?因爲這裏沒有從ah的有效隱式轉換。

lambdas的轉換不是編譯器的魔力。該標準簡單地說,對於非捕獲的非泛型lambdas,lambda閉包類型具有用於匹配其過載的簽名的函數指針的隱式轉換運算符。初始化int (*g)(int)a許可使用隱式轉換的規則,因此編譯器將調用該運算符。

auto不允許使用隱式轉換運算符;它需要按原樣(當然,除去引用)。 auto*也不會執行隱式轉換。那麼爲什麼它會爲lambda封閉而不是用戶定義的類型調用隱式轉換呢?

5

拉姆達代碼不爲同一工作的原因所在,這並不工作:

struct foo { 
    operator int*() const { 
    static int x; 
    return &x; 
    } 
}; 

int* pint = foo{}; 
auto* pint2 = foo{}; // does not compile 

甚至:

template<class T> 
void test(T*) {}; 
test(foo{}); 

拉姆達具有隱含其轉換爲一個操作員(特別)函數指針,就像foo

auto不做轉換。永遠。自動的行爲就像一個class T參數給一個模型函數,其類型被推導出來。

由於右側的類型不是指針,因此不能用於初始化變量auto*

Lambdas不是函數指針。 Lambdas不是std::function s。它們是自動編寫的功能對象(具有operator()的對象)。

檢查此:

void (*ptr)(int) = [](auto x){std::cout << x;}; 
ptr(7); 

它編譯和GCC工作(不能肯定,如果它是一個擴展,現在,我想它)。但是,auto* ptr = [](auto x){std::cout << x;}應該做什麼?

但是,一元運算符+是一個對指針(對它們幾乎沒有任何作用)的運算符,但不在foo或lambda中。

所以

auto* pauto=+foo{}; 

而且

auto* pfun=+[](int x){}; 

兩個工作,神奇。

+0

「不確定,如果它是一個擴展名」我希望能工作。你可以把一個特定的函數指針指向一個函數模板(例如'template void foo(T){} void(* p)(int)= foo;' – Barry

+0

@Barry當然,我希望這樣做,但'auto' lambdas我甚至想隱式轉換爲任何兼容轉換的函數,所以即使是void(* f)(double)= [](int x){std :: cout <<; x;};'會編譯(就像它用'std :: function Yakk