[英]std::functions and lambda function passing
我有一個類,它將std::function
作為參數,我分配了一個lambda函數。 它在構造函數中工作,但之后停止工作。 調試器在運行第一行后表示f
為“空”。 為什么?
#include <iostream>
#include <string>
#include <functional>
typedef std::function<void(std::string)> const& fn;
class TestClass
{
public:
TestClass(fn _f) : f(_f) { F(); }
void F() { f("hello"); };
private:
fn f;
};
int main()
{
TestClass t([](std::string str) {std::cout << str << std::endl; });
t.F();
return 0;
}
調用tF()
會導致錯誤。 為什么?
我可以通過將其更改為以下內容來解決此問題:
int main()
{
fn __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
return 0;
}
但是,當我將fn
更改為auto
時,這不起作用!
int main()
{
auto __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
return 0;
}
為什么會發生這種情況的原因是什么?
注意(1) fn
定義為引用 (對const); (2)lambda和std::function
的類型不一樣; (3)您不能直接綁定對不同類型的對象的引用。
對於第一種情況,
TestClass t([](std::string str) {std::cout << str << std::endl; });
t.F();
創建一個臨時lambda,然后轉換為std::function
,這也是一個臨時的。 臨時std::function
綁定到構造std::function
的參數_f
並綁定到成員f
。 在此聲明之后,臨時將被銷毀,然后當tF();
時f
變為懸空tF();
它失敗。
對於第二種情況,
fn __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
創建臨時lambda然后綁定到引用(到const)。 然后它的生命周期延長到引用__f
的生命周期,所以代碼很好。
對於第三種情況,
auto __f = [](std::string str) {std::cout << str << std::endl; };
TestClass t(__f);
t.F();
創建lambda然后轉換為std::function
,這是一個臨時的。 臨時std::function
綁定到構造std::function
的參數_f
並綁定到成員f
。 在此聲明之后,臨時將被銷毀,然后當tF();
時f
變為懸空tF();
它失敗。
(1)你可以將fn
聲明為非引用,如typedef std::function<void(std::string)> fn;
,然后將復制std::function
並且每個案例都能正常工作。
(2)不要使用以雙下划線開頭的名稱,它們是在C ++中保留的。
typedef std::function<void(std::string)> const& fn;
這不是std::function
,它是對std::function
的引用。
TestClass(fn _f) : f(_f) { F(); }
fn f;
在這里,你將一個const&
一個std::function
綁定到另一個const&
一個std::function
。 構造函數體中的F()
起作用,因為引用至少與構造函數一樣有效。
TestClass t([](std::string str) {std::cout << str << std::endl; });
這將創建一個從lambda創建的std::function
臨時文件。 這個臨時持續時間與當前行一樣長(直到;
)。
然后丟棄臨時std::function
。
由於TestClass
通過const&
獲取std::function
,因此它不會延長臨時生命周期。
所以在行之后,對std::function const&
任何調用都是未定義的行為,你在稍后調用.F()
會看到這種行為。
fn __f = [](std::string str) {std::cout << str << std::endl; };
這確實參考了壽命延長。 從lambda創建的臨時std::function
的生命周期延長到__f
變量的生命周期。
順便說一句,這一行也會使你的程序形成錯誤,不需要診斷,因為它有一個包含雙下划線的變量。 這些標識符保留用於編譯器的實現,您可能無法創建它們。
TestClass t(__f);
然后我們傳遞這個引用(指的是生命周期擴展臨時),一切正常。
auto __f = [](std::string str) {std::cout << str << std::endl; };
這會創建一個變量__f
(參見上面的錯誤名稱),它是一個lambda。
lambda不是std::function
。 可以隱式地從lambda創建std::function
。
TestClass t(__f);
這將從lambda創建一個臨時的std::function
,將其傳遞給TestClass
構造函數,然后銷毀臨時的。
在此行之后,對.F()
的調用最終會在懸空引用之后,並且會導致未定義的行為。
你的核心問題可能是你認為lambda是一個std::function
。 它不是。 std::function
可以存儲lambda。
你的第二個問題是將某個東西定義為const&
,這幾乎總是一個非常愚蠢的想法。 引用的行為與基本方式的值不同。
您的第三個問題是變量名稱中的雙重修正。 (或以_
后跟大寫字母開頭的標識符)。
如果你想知道std::function
如何工作以及它是什么,那么在這個主題上有很多好的SO帖子,有各種級別的技術細節。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.