一文帶你認識 C++ 中的 Lambda 函數

2021-02-21 CPP開發者

(給CPP開發者加星標,提升C/C++技能)

英文:Vishal Chovatiya,翻譯:CPP開發者/張宗豪

【導讀】:Lambda函數是 Modern C++在C++11中的一個體現 ,雖然在網絡上已經有很多關於lambda函數的討論或者教學,但是仍然有一部分內容(例如IIFE,lambda類型等)沒人談論。因此在這裡,我不僅要向您展示C++中的lambda函數,而且還將介紹Lambda的工作原理以及發展。

以下是正文


什麼是lambda函數?

本文標題有點誤導。因為 lambda並不總是函數指針,它一個表達式。但是為了簡單起見,我一直都稱它為函數。那麼在本文中,我將會交替使用lambda函數和表達式來稱呼。

Lambda函數是一段簡短的代碼片段,它具有以下特點:

(1)不值得命名(無名,匿名,可處理等,無論您如何稱呼)

(2)不會重複使用

換句話說,它只是語法糖。lambda函數的語法定義為:

[ capture list ] (parameters) -> return-type {      method definition}

通常, 編譯器會評估lambda函數本身的返回類型。因此,我們不需要顯式指定返回類型,即 -> return-type,但是在某些複雜的情況下,編譯器無法推斷出返回類型,此時我們需要指定返回類型。

為什麼要使用lambda函數?

 C++包括許多有用的通用函數,例如 std::for_each,通常情況下方便了我們的使用。但是,當需要考慮特殊需求的時候,就比較頭疼了。如下代碼所示:

struct print{    void operator()(int element){        cout <<element << endl;    }};int main(void){    std::vector<int> v = {1, 2, 3, 4, 5};    std::for_each(v.begin(),v.end(), print());    return 0;}

如果您只在某個特定位置使用一次print,卻寫了一個類。這樣的操作代價似乎太大了。

但是,對於這種情況,內聯代碼將更合適,並且可以通過如下的lambda函數來實現:

std::for_each(v.begin(), v.end(), [](int element) { cout <<element << endl; });

lambda函數工作原理

[&i] ( ) { std::cout << i; }

struct anonymous{ int &m_i;   anonymous(int &i) :m_i(i) {}   inline autooperator()()const{     std::cout << i; }};

如代碼所示,編譯器為每一個lambda函數生成獨特的閉合。捕獲列表將成為閉包中的構造函數參數,如果按值捕獲,則會在閉包中創建相應類型的數據成員。此外,您可以在lambda函數參數中聲明變量/對象,它將成為調用運算符的參數,即operator()。

使用Lambda函數的好處

(1)零成本抽象。是的!  你沒看錯。lambda不會犧牲性能,運行速度和普通函數一樣快。

(2)此外,代碼會變得更加緊湊,結構層次更加明顯和代碼可讀性更佳。

學習lambda表達式


1.通過引用/值捕獲

int main(){    int x = 100, y = 200;    auto print = [&]{         std::cout <<__PRETTY_FUNCTION__ << " : " << x<< " , " << y << std::endl;    };    print();    return 0;}

輸出:

main()::<lambda()> : 100 , 200

在上面的示例中,我在捕獲列表中用到了 &。通過引用的方式捕獲變量 x和 y。同樣,= 表示按值捕獲,這將在閉包內創建相同類型的數據成員,同時賦上相同的值。需要注意的是,參數列表是可選的, 如果不將參數傳遞給lambda表達式,則可以省略空括號。

2.Lambda捕獲列表

2.1 將lambda作為參數傳遞

template <typename Functor>void f(Functor functor){    std::cout <<__PRETTY_FUNCTION__ << std::endl;}int g() { static int i = 0; return i++; }int main(){    auto lambda_func =[i = 0]() mutable { return i++; };    f(lambda_func);     f(g);           }

輸出:

Function Type : void f(Functor) [with Functor = main()::<lambda(int)>]Function Type : void f(Functor) [with Functor = int (*)(int)]

您也可以將lambda函數作為參數傳遞給其他函數,就像我上面編寫的普通函數一樣。相信您注意到了,我在捕獲列表中聲明了變量i,它將成為數據成員。所以,每次調用lambda_func時,它將被返回並遞增。

2.2lambda捕獲this指針或成員變量

class Example{public:    Example() : m_var(10) {}    void func(){        [=]() { std::cout << m_var<< std::endl; }();     }private:    int m_var;};int main(){    Example e;    e.func();}

捕獲this指針也可以使用 [this], [=]或者 [&]。在上述任何情況下,類內數據成員(包括 private)的訪問方式與常規方法一樣。

可以看到lambda表達式的末尾,我多寫了一個 ( ),該函數通常在聲明之後立刻對其進行調用。它稱為IIFE(立即調用函數表達式)。

C++ lambda函數類型


1.通用lambda

const auto l = [](auto a, auto b, auto c) {};struct anonymous{    template <class T0, class T1, class T2>    auto operator()(T0 a, T1 b, T2 c) const    {    }};

C++ 14中引入的通用lambda可以使用auto說明符。

2.可變參數通用λ

void print() {}template <typename First, typename... Rest>void print(const First &first, Rest &&... args){    std::cout << first<< std::endl;    print(args...);}int main(){    auto variadic_generic_lambda = [](auto... param) {        print(param...);    };    variadic_generic_lambda(1, "lol", 1.1);}

具有可變參數的Lambda在許多情況下非常有用,例如調試,使用不同的數據輸入重複操作等。

3.mutable lambda函數

通常,lambda的函數調用運算符是const-by-value,這意味著lambda需要捕獲可變值的 關鍵字時,需要使用mutable關鍵字。

[]() mutable {}

struct anonymous{ auto operator()() { }};

4.Lambda作為函數指針

#include<iostream>#include<type_traits>
int main(){ auto funcPtr = +[] {}; static_assert(std::is_same<decltype(funcPtr), void (*)()>::value);}

您可以通過添加命令「+」來強制編譯器將lambda生成為函數指針而不是閉包。

5.lambda函數作為返回值

const auto less_than = [](auto x) {    return [x](auto y) {        return y < x;    };};
int main(void){ auto less_than_five = less_than(5); std::cout << less_than_five(3) << std::endl; std::cout << less_than_five(10) << std::endl; return 0;}

再進一步,lambda函數還可以返回另一個lambda函數。這也為代碼的自定義性,代碼可讀性和緊湊性帶來無限可能。

6.constexpr lambda表達式

從C ++ 17開始,可以將lambda表達式聲明為 constexpr。

constexpr auto sum = [](const auto &a, const auto &b) { return a + b; };constexpr int answer = sum(10, 10);

即使你沒有指定 constexpr,如果它恰好滿足所有 constexpr函數的要求,那麼它也會被聲明為constexpr。

結束語


希望您喜歡這篇文章。文中我使用幾個簡單的小例子來介紹關於lambda的大量複雜問題。考慮到代碼的可表達性和易維護性,無論您想到什麼,都應該使用lambda,就像可以在自定義刪除器中將其用於智能指針和大多數STL算法一樣。

https://hackernoon.com/all-about-lambda-functions-in-cfrom-c11-to-c17-2t1j32qw

- EOF -

關於C++lambda,歡迎在評論中和我探討。覺得文章不錯,請點讚和在看支持我繼續分享好文。謝謝!


關注『CPP開發者』

看精選C++技術文章 . 加C++開發者專屬圈子

↓↓↓

點讚和在看就是最大的支持❤️

相關焦點

  • C++中的lambda函數
    作者:  2019信息與計算科學專業   楊澤天lambda函數是C++11中的匿名函數,又叫lambda表達式,叫lambda表達式更好理解,因為函數是不可以在函數中定義的,表達式可以。lambda表達式,可以簡化編程工作。
  • C++ lambda表達式簡介及作用
    C++ lambda表達式簡介及作用C/C++的可調用對象在C語言中,可調用對象僅有函數指針。但在C++中,可調用對象增加了兩類,其一是利用C++操作符重載實現的functor,functor本身是一個struct/class的實例,其特殊的地方在於重載了小括號(調用)操作符其二則是C++2.0引入的lambda表達式,也成為匿名函數,其語法為// lambda[ <捕獲列表> ] ( <參數列表>
  • Python中的lambda函數
    匿名函數是指沒有聲明函數名稱的函數。儘管它們在語法上看起來不同,lambda函數的行為方式與使用def關鍵字聲明的一般函數相同。以下是Python中 lambda函數的特點:在本文中,我們將詳細討論Python中的lambda函數,並演示使用它們的例子。
  • Python每天一分鐘:lambda表達式 (匿名函數)及用法詳解
    lambda表達式介紹python中有一種靈活,便捷的且具有函數功能的表達式:lambda表達式!lambda 表達式可以用來替換局部函數(感興趣的讀者可以自行查閱「局部函數」),下面為大家演示lambda表達式的具體用法。lambda表達式定義首先以一個代碼例子讓大家對lambda表達式有一個直觀的認識:
  • lambda與函數式
    和響應式流的特性,一味講概念終是枯燥,還是上手敲一敲代碼實在感受一下響應式編程的「手感」吧。(3)lambda與函數式——響應式Spring的道法術器這一節,我們先了解一下lambda與函數式(已經了解的朋友可以直接跳到1.3.2),熟悉一下如何使用Reactor進行響應式編程,然後使用Spring Boot2,基於Spring 5的Webflux和Reactive Spring Data逐步開發一個「Hello world」級別的RESTful service。
  • Python中的4個Lambda函數示例
    對序列排序Lambda函數的第一應用場景就是用於內置函數sorted()中,用於對序列類型的數據進行排序。如果你熟悉了列表對象的方法,你應該知道它有一個類似的方法sort(),通過它能夠實現對列表對象的原地排序。
  • C++並發與多線程__C++如何線程創建線程以及函數join()和detach()用法和區別
    前言:通常一個程序運行起來,也就等於一個進程在運行,這個進程中會有一個主線程自動創建並運行,當程序的main()函數返回之後那麼此主線程也就運行結束
  • 【C++基礎】C++11 lambda 表達式解析
    C++11 新增了很多特性,lambda 表達式是其中之一,如果你想了解的 C++11 完整特性,建議去看看C++標準。本文作為 5 月的最後一篇博客,將介紹 C++11 的 lambda 表達式。很多語言都提供了 lambda 表達式,如 Python,Java 8。
  • 詳細講解:python中的lambda與sorted函數
    lambda表達式python中形如:lambda parameters: expression稱為lambda表達式,用於創建匿名函數,該表達式會產生一個函數對象。該對象的行為類似於用以下方式定義的函數:def <lambda>(parameters): return expressionpython中的lambda函數可以接受任意數量的參數,但只能有一個表達式。也就是說,lambda表達式適用於表示內部僅包含1行表達式的函數。
  • 一文帶你認識超級壽星
    一文帶你認識超級壽星時間:2020-12-25 16:59   來源:今日頭條   責任編輯:沫朵 川北在線核心提示:原標題:超級壽星的植物是什麼? 一文帶你認識超級壽星 超級壽星的植物是什麼 關於這些你知道嗎?超級壽星的植物是什麼,具體詳情如下: 1、超級壽星的植物是百歲蘭。 2、百歲蘭是百歲蘭科百歲蘭屬植物。
  • 現代C++函數式編程之function compose
    return reduce(lambda f, g: lambda x: f(g(x)), funcs)f = lambda x: x + 1g = lambda x: x * 2h = lambda x: x - 3print(compose(f, g, h)(10))compose(f,g,h)(10)等價於 f(g(h(10))), 語義還是比較簡單的
  • python之lambda函數使用
    一,lambda函數的概念lambda函數被稱為匿名函數,實際就是說沒有名稱的函數,形式如下:
  • Python匿名函數:Lambda表達式
    我們以一張圖形進入主題:從圖中我們可以看出lambda表達式幾點特徵:簡潔性,符合了Python的一貫宗旨;起到了函數的作用,但未顯示函數名稱,這就是匿名函數;有形參;有返回值的。【2】Lambda表達式如何實現函數功能?
  • 通過「四不要」,掌握 Python 的 Lambda 函數
    當你需要完成一件小工作時,在本地環境中使用它們可以讓工作得心應手。有些人將它們簡稱為 lambdas,它們的語法如下:lambda arguments: expressionlambda 關鍵字可以用來創建一個 lambda 函數,緊跟其後的是參數列表和用冒號分割開的單個表達式。
  • 記住「四不要」,掌握 Python 的 Lambda 函數
    當你需要完成一件小工作時,在本地環境中使用它們可以讓工作得心應手。有些人將它們簡稱為 lambdas,它們的語法如下:lambda arguments: expressionlambda 關鍵字可以用來創建一個 lambda 函數,緊跟其後的是參數列表和用冒號分割開的單個表達式。
  • python入門基礎之lambda匿名函數詳解
    lambda作為一個表達式,定義了一個匿名函數,上例的代碼x,y為入口參數,x+y為函數體。在這裡lambda簡化了函數定義的書寫形式。python允許用lambda關鍵字創造匿名函數。匿名是不需要以標準的方式來聲明,比如說使用 def 語句。(除非賦值給一個局部變量,這樣的對象也不會在任何的名字空間內創建名字,上面的例子中會創建名字。)作為函數,它們也能有參數。
  • Python中函數的定義、傳參和lambda函數是什麼?他們有什麼用?
    今天我們來學習函數的定義、傳參和lambda函數。開發工具:IDLE (Python 3.7 64-bit或者32-bit) (註:不需要3.7版本的也行,只需要Python3的版本都可以,在語法方面沒有什麼不同。)
  • Python中的Lambda表達式
    Lambda表達式當我們需要做一些簡單的事情並且更希望快速完成工作而不是正式命名函數時,Lambda表達式是理想的選擇。Lambda表達式也稱為匿名函數。Python中的Lambda表達式是聲明小型匿名函數的一種簡短方式(沒有必要為Lambda函數提供名稱)。
  • 不要在Python中編寫 lambda 表達式了
    當經驗豐富的 Python 程式設計師看到一個lambda 表達式時, 他們知道他們正在使用一個僅在一個地方有效的函數, 並且只做一件事情.如果你曾經在 JavaScript 中使用過匿名函數, 那麼Python 中的 lambda 表達式與之相同, 除了具有更多限制以及與傳統函數完全不同的語法.
  • C++ 優先隊列priority_queue
    其實排序這個底層邏輯你是不用管的,你只要把想要的數據放到優先隊列裡,然後取出的必定是當前狀態下最優的,當然,究竟什麼是最優的條件是需要你來設定的,也就是說我們需要定義排序的規則。頭文件優先隊列 priority_queue 是隊列 queue 的一個變種,頭文件是#include <queue>,使用優先隊列必須要包含這個頭文件。