這是第四篇,也是最後一篇談 Lambda。Lambda 之所以耐談,是因為好用;Lambda 之所以要多談,是因為容易誤用。開發者(我也在其中)常犯的毛病之一—手上有榔頭,看什麼都像是像釘子。好像全世界的問題都可以用同一個方法處理,這,當然是錯的。
實務遠比紙上談兵複雜,是否使用 Lambda,怎麼使用,請審慎評估。
Lambda 在 C++11 首現身,三年後的 C++14 又做了些許強化。其中,較為有用的是 Generic Lambda,以及 Initialized lambda capture(init-capture)。
考慮以下程式碼:
std::vector<int> vi = {1, 2, 3, 4, 5, 6};
auto sum = std::accumulate(begin(vi), end(vi),
0,
[](int v1, int v2)
{
return value > 4;
});
上述的 Lambda 只支援 std::vector<int>
,若容器型別為 std::vector<double>
,但邏輯不變,則另寫一個版本似乎有些「過意不去」。所幸,C++ Generic 的特性,在 C++14 時,加入了 Generic Lambda。上述程式碼改寫如下:
std::vector<double> vi = {1.0, 2.2, 3.4, 4.2, 5/2, 6/9};
auto sum = std::accumulate(begin(vi), end(vi),
0,
[](auto v1, auto v2) // Use 'auto' instead of type
{
return value > 4;
});
關於 Lambda,這裡有一篇優質文章,一步步拆解 C++ Lambda 的用法,非常推薦閱讀。
先前用到 Lambda Capture Clause 時,總是「抓」已經存在的變數,C++14 加入了 Initialized lambda capture,讓我們在 Lambda 內部使用被抓進來的對象前,做一些手腳。
std::vector<int> vi = {10, 20, 30, 40, 50};
const int kTarget = 3;
auto it = std::find_if(begin(vi), end(vi),
[target = kTarget * 10](auto value)
{
return target == value;
});
上述 target
的值為 30。有了 [id = expression]
的用法,Lambda 的應用場景更廣了。
雖然還沒談到 std::move
,Lambda 遇到 std::unique_ptr
時,有一個用法很重要:
{
std::vector<std::string> owners = {"COCO", "50", "1Shit"};
auto owner = std::make_unique<std::string>("1Shit");
auto the1shit = std::find(begin(owners), end(owners),
[the_one = std::move(owner)](auto o)
{
return *the_one.get() == o;
});
}
留意我們在 Capture Clause 裡先「移動」 owner
至 the_one
,然後再給 Lambda 內部使用。(後面會有專篇介紹 std::move
),這麼做就不用擔心 owner
離開 Scope 後被清掉,因為其擁有權已經轉移至 the_one
。
Lambda 遇到 std::unique
std::unique_ptr
時
修改囉,感謝訂正。