iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
0
Software Development

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

DAY 12:Lambda,卷二:Capture Clause

Lambda 是 Modern C++ 的主要功能之一,對於 C++ 程式碼的改善有決定性的影響。想要掌握 Modern C++,把 Lambda 學好、弄熟是必要條件。上一篇簡單介紹 Lambda,這一篇把重點放在 Capture Clause,也就是下圖的前端,藍色區塊:

Lambda 使得好,Capture 很重要。理解 Capture Clause 需要對 C++ Scope 有正確的認知,建議先複習

底下程式碼展示未使用 Lambda 叫用 STL Algorithm 的一種寫法:


bool AdmitTaiwanIsPartOfChina(const std::string& name)
{
  if (name == "One Smell" || name == "COCO" || name == "Milkshake")
    return true;
  return false;
}

std::vector<TeaShopOwner> owners = {"One Smell", "COCO", "Fifty", "Milkshake"};
std::vector<TeaShopOwner> shitters;

std::copy_if(begin(owners), end(owners),
             std::back_inserter(shitters),
             AdmitTaiwanIsPartOfChina);

如同「手搖飲」公開承認台灣屬中國一部分,有些人覺得沒什麼問題,有些人覺得大有問題。 上述程式碼乍看之下好像沒什麼問題,但使用 AdmitTaiwanIsPartOfChina 這類 Predicate 有一些限制,諸如缺少內部狀態、參數限制為一個(不考慮 Binders)。

上述程式碼可用 Lambda 改寫:

std::vector<TeaShopOwner> owners = {"One Smell", "COCO", "Fifty", "Milkshake"};
std::vector<TeaShopOwner> shitters;

std::copy_if(begin(owners), end(owners),
             std::back_inserter(shitters),
             [](const std::string& name)
{
  if (name == "One Smell" || name == "COCO" || name == "Milkshake")
    return true;
  return false;
});

上述版本的「好處」之一是,std::copy_if 的 Predicate 邏輯直接寫在呼叫處。接下來,假設 std::copy_if Predicate 內部的邏輯需要來自外部的資料,此時,便可使用 Capture 來「抓取」。為簡化程式碼,底下用其他範例來展示。

C++ Lambda Capture 有幾種模式:

  • Capture by-value
  • Capture by-reference

寫法上,又分兩種:

  • capture-default
  • individual capture

Capture-default by-value 用一個 = 符號表示:

std::vector<int> sources = {1, 2, 3, 4, 5, 6, 7, 8};
int minimal = 6;
auto count = std::count_if(begin(sources), end(sources),
                           [=](int s)
                           {
                               return s > minimal;
                           });

上例中,sources 以及 minimal 皆被「抓取」至 Lambda 中成為可見,而且是以 By-value 的形式抓取,意思是會產生一個 minimal 的複製品供 Lambda 使用,作用於 Lambda 裡的 minimal 不會影響外部那個。此寫法即為 capture-default。不過,由於 Lambda 內部只用到了 minimal,因此,比較好的寫法是 individual capture

std::vector<int> sources = {1, 2, 3, 4, 5, 6, 7, 8};
int minimal = 6;
auto count = std::count_if(begin(sources), end(sources),
                           [minimal](int s)
                           {
                               return s > minimal;
                           });

By-value 的使用要特別留意「物件複製的成本」,成本太高而且該片段會被頻繁使用的話,可以利用 By-reference 的方式傳遞。下一篇就來談 Capture by-reference。

延伸閱讀


上一篇
DAY 11:Lambda,卷一:讓 STL 更親民的 Lambda 簡介
下一篇
DAY 13:Lambda,卷三:Capture By-reference
系列文
山姆大叔談 C++:從歷史談起,再給個定義—Modern C++ 解惑26
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言