C++ 的lambda

lambda表達(dá)式簡(jiǎn)稱(chēng)lambda式,是C++11 新添加的特性。它適合表達(dá)簡(jiǎn)短、短期調(diào)用的可調(diào)用對(duì)象。

看effective modern c++時(shí),對(duì)lambda的簡(jiǎn)單用法做個(gè)記錄。

1. lambda原型如下:

[captures](params) mutable exception attribute {body}
其中,

  • captures是在lambda函數(shù)體中可以訪問(wèn)的外部變量,有兩種默認(rèn)捕獲模式:按值捕獲和按引用捕獲
  • params指定表達(dá)式的參數(shù)
  • mutable修飾符表示lambda函數(shù)可以修改捕獲的變量,并調(diào)用對(duì)象的非const方法
  • exception告訴編譯器函數(shù)是否拋異常(noexcept)
  • attribute用來(lái)聲明屬性

lambda的返回值:如果函數(shù)中有return語(yǔ)句,則類(lèi)型從return后面的表達(dá)式推導(dǎo)而來(lái);如果沒(méi)有return語(yǔ)句,則類(lèi)似于void。

關(guān)于capture的捕獲模式: [a, &b] 表示a按值捕獲,b按引用捕獲;[]表示不捕獲任何外部變量

2. lambda有哪些用法?

  1. STL中"_if"函數(shù)族,比如
std::find_if(container.begin(), container.end(), [](int val){ return val>0})
  1. 自定義比較函數(shù)(std::sort, std::nthelement, std::lower_bound)
  2. std::unique_ptr, std::shared_ptr自定義解析器
  3. 用lambda實(shí)現(xiàn)閉包

lambda與閉包

  1. 閉包是lambda表達(dá)式的運(yùn)行期對(duì)象,根據(jù)不同的捕獲模式,閉包會(huì)持有數(shù)據(jù)的副本或引用。在上面find_if函數(shù)中,閉包就是作為第三個(gè)實(shí)參在運(yùn)行期傳遞給find_if函數(shù)的。
  2. 閉包類(lèi)就是實(shí)例化閉包的類(lèi)。每個(gè)lambda表達(dá)式都會(huì)觸發(fā)編譯器生成獨(dú)一無(wú)二的閉包類(lèi)。而閉包中的語(yǔ)句會(huì)變成它的閉包類(lèi)成員函數(shù)的可執(zhí)行指令。
    就存在期而言,lambda和閉包類(lèi)存在于編譯期,閉包存在于運(yùn)行期。
    閉包屬于可調(diào)用對(duì)象,因此可以像變量一樣對(duì)它賦值。

lambda的簡(jiǎn)單使用

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

int main()
{
  vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  int threshold = 5;
 
  // 刪除小于threshold的元素
  std::remove_if(v.begin(), v.end(), [&threshold](int v){return v<threshold;});
  for_each(v.begin(), v.end(), [](int v){std::cout<<v<<"\t";});
  
  // lambda賦給變量, 只能用auto修飾
  auto f1 = [](int v){ return v*2;};

  // 由于lambda也是一種可調(diào)用對(duì)象
  // 因此,也可以用function來(lái)接收
  std::function<int(int)> f2 = [](int v){return v+1;};
  std::cout<<f2(2)<<std::endl;
 }

引用懸掛問(wèn)題

默認(rèn)按引用捕獲的一個(gè)可能問(wèn)題是, 引用懸掛。即按引用捕獲會(huì)導(dǎo)致包含指向局部變量的引用,當(dāng)lambda離開(kāi)局部作用域時(shí),會(huì)導(dǎo)致引用的那個(gè)變量被釋放,lambda里的那個(gè)引用發(fā)生"懸掛"。

 using func_v = std::vector<std::function<bool(int)>>;
 
 func_v filters;
 ...
 void addFilters()
 {
   int threshold = 5; // 局部變量
   filters.emplace_back([&threshold](int v){return v<threshold;});    
 }// threshold已經(jīng)被銷(xiāo)毀,但是filters中仍保留著對(duì)它的引用
 filters.doSomething();  // 未定義行為

對(duì)于上面這個(gè)問(wèn)題來(lái)說(shuō),傳值足以解決問(wèn)題。但這并不表示,按值傳遞就是安全的,因?yàn)閭鬟f指針也是按值傳遞的一種。

當(dāng)我們把一個(gè)指針(按值傳遞)傳遞給lambda(其實(shí)是所有可調(diào)用對(duì)象)時(shí),你不知道當(dāng)你在這個(gè)函數(shù)里興高采烈地大展拳腳時(shí),在函數(shù)外面可能某個(gè)”不良分子“把指針指向的對(duì)象悄悄地釋放掉了。這段話(huà)是為了說(shuō)明按值捕獲也許并沒(méi)有想象中比按引用捕獲安全到哪里去。

那么怎么解決這個(gè)問(wèn)題呢?C++14提供了廣義lambda捕獲,它將你想要捕獲的變量做一個(gè)副本,然后在lambda里使用這個(gè)副本。

 void addFilters()
 {
   int threshold = 5; // 仍是局部變量
   filters.emplace_back([threshold = threshold](int v){return v<threshold;});
 // [threshold = threshold]將threshold復(fù)制入閉包,lambda內(nèi)部使用副本
  }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容