iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 14
0
Software Development

山姆大叔談 C++:從歷史談起,再給個定義—Modern C++ 解惑系列 第 14

DAY 13:Lambda,卷三:Capture By-reference

前一篇提到 Lambda Capture by-value。這一篇說明 Capture by-reference——以 & 表示。

同樣分成兩種寫法:

  • default-capture
  • individual capture

底下程式碼用於展示 Lambda 的語法,其功能性不在討論範圍,切記。

std::vector<int> iv = {1, 1, 1, 2, 3, 4, 5, 6, 8};
int call_count = 0;

auto it = std::find_if(begin(iv), end(iv),
                       [&](int i)
{
    ++call_count;
    return i > 6;
}

因為 [&] 的關係,上例中 Lambda 內的 call_count 為外部的一個參考,改變 call_count 的值,等於改變了外面的那個 call_count 的值。

上例中,call_count 以及 iv 皆可被 Lambda 以 by-reference 的方式使用,但其實只有 call_count 有用到。因此,上述例子可以改成以下較為安全的寫法:

std::vector<int> iv = {1, 1, 1, 2, 3, 4, 5, 6, 8};
int call_count = 0;

auto it = std::find_if(begin(iv), end(iv),
                       [&call_count](int i)
{
    ++call_count;
    return i > 6;
}

如此一來,iv 確定無法被 Lambda 使用,這樣的「保證」在理解程式碼結構,以及問題發生時的除蟲,皆有助益。我建議沒有特殊考量的話,應優先使用 individual capture。

由於 Capture by-reference 可以參考到外部變數,在某種情況下,Lambda 被執行時已經離開該變數所在的 Scope,使用該變數會造成程式出問題,甚至當掉。因此,每用 Capture by-reference 時,都要先問:「Lambda 何時被執行?」

std::thread 為例:

std::vector<int> iv = {1, 1, 1, 2, 3, 4, 5, 6, 8};
int call_count = 0;

std::thread the_thread([&call_count, &iv]()
{
	auto it = std::find_if(begin(iv), end(iv),
	                       [&call_count](int i)
	{
	    ++call_count;
	    return i > 6;
	}
});

the_thread.detach();

the_thread 執行時,call_count 以及 iv 已經不存在,不應該被使用。


上一篇
DAY 12:Lambda,卷二:Capture Clause
下一篇
DAY 14:Lambda,卷四:好東西也要用得恰到好處
系列文
山姆大叔談 C++:從歷史談起,再給個定義—Modern C++ 解惑26
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
ekids1234
iT邦新手 5 級 ‧ 2023-04-21 02:42:43

想請問: 最後一個例子,call_count 以及 iv 不存在的原因是 ?
他不是先傳到一個 lambda 然後再傳到下一個 labmda ?

我的觀點是
std::thread the_thread([&call_count, &iv]()
被執行的時候已經不在原本有 call_count 以及 iv 的Task Struct了
所以才沒有這兩變數

我要留言

立即登入留言