C++ lambda函數詳解

c++中lambda函數是作爲c++11新特新添加到c++中的,其主要是以匿名函數捕獲scope內變量的方式構造閉包(closure)。相關標準參考:c++ reference
下面以若干不相干的簡短程序的方式總結一下lambda的基本使用方式(暫未涉及lambda的c++17/20的特性)

[lambda.h]
#include<iostream>
#pragma once
using namespace std;
void funcExt();
class Lamb{
    private:
        int aa =20;
        int bb =30;
    public:
        Lamb();
        ~Lamb();
        void show();
        void f(){
            auto fun10 = [this](){this->show();};
            fun10();
        }
};
---------------------------------------------------------------------------------------------------------
[lambda_ext.cpp]
#include "lambda.h"
auto func =[](){cout<<"funcExt result."<<endl;};
void funcExt(){
    func();
}
Lamb::Lamb(){};
Lamb::~Lamb(){};
void Lamb::show(){
    cout<<aa+bb<<endl;
}
---------------------------------------------------------------------------------------------------------
[lambda.cpp]
// [ captures ] <tparams>(optional)(C++20) (params) specifiers(optional) exception attr 
//              -> ret requires(optional)(C++20) { body }
      // tparams 模板形參列表,提供名稱給泛型lambda的模板形參(c++20)
      // specifiers [mutable/constexpr(c++17)]
// [ captures ] ( params ) -> ret { body }
// [captures](params) { body }
// [ captures ] { body }
//      僅若不使用specifiers、exception、attr或ret之一才能使用此形式。

#include <iostream>
#include <typeinfo>
#include "lambda.h"

int ext = 10 ;

int main()
{
    // normal
    auto func1 = []() { cout << "func1:hello world." << endl; };
    func1();
    // var func1 type Z4mainE3$_1 ,only auto

    // ignore params
    auto func2 = [] { cout << "func2:haha." << endl; };
    func2();

    // spec ret
    auto func3 = []() -> int { return 1.0; };
    cout << "func3 ret:" << typeid(func3()).name() << "=" << func3() << endl;

    // mutil return spec ret
    auto func4 = [](int i) -> int {
      if(i>10) return 1;
      else return 2.3;
    };
    cout << "func4 ret:" << typeid(func4(5)).name() << "=" << func4(5) << endl;
    funcExt();

    // normal para_list lambda,compared to func9() and func7()
    int a=5,b=6;
    auto func5 = [](int& x,int& y){x++,y++;return x+y;};
    cout << "func5 sum:" << func5(a,b) << " a:" << a << " b:" << b << endl;
    // capture
    // capture opt: -- only capture non-static var
        // blank 不捕獲任何變量
        // = 捕獲外部作用域所有自動變量,按值傳遞給函數體,且包含this捕獲,如果存在對象
        // & 捕獲外部作用域所有自動變量,按引用傳遞給函數體,且包含this捕獲,如果存在對象
        // this 按引用捕獲當前對象
    {
      int c=7;
      {
        int e=9;
        {int f=11;/*static int ext=10;*/}
        auto func6 = [a, c, e, ext/*f*/]() { // capture f is illegal,not in lambda scope
                     // ext is static ,not captured, but capture list allowed -- warning
          cout<<"func6 a:"<<a<<" c:"<<c<<" e:"<<e<<" ext:"<<ext<</*" f:"<<f<<*/endl;};
        func6();
      }
      auto func7 = [=,&a](){ // capture e is illegal,not in lambda scope
          cout<<"func7 a:"<<a<<" c:"<<c<</*" e:"<<e<<*/" ext:"<<ext<<endl;
          a++;/*c++;*/}; // var copy ,change not access
      func7();
      cout<<"a++: "<<a<<endl;
      auto func8 = [](){
          cout<</*"a:"<<a<<" c:"<<c<<" e:"<<e<<*/"func8 ext:"<<ext<<endl;
          ext =11;};
      func8();
      cout<<"access static ext+1: "<<ext<<endl;
      // lambda operator() is const(copy capture) , val capture non-modifiable
      auto func9 = [=]()mutable{
      c++;};
      cout<<"c: "<<c<<endl;
      func9();
      cout<<"func9 c++: "<<c<<endl; // capture var copy
    }
    Lamb func;
    func.f();
    // diff between = and &
    auto func11 = [=](){return a;};
    cout<<"a: "<<a;
    a++;
    cout<<" func11 a++: "<<func11()<<endl;
    auto func12 = [&](){return b;};
    cout<<"b: "<<b;
    b++;
    cout<<" func12 b++: "<<func12()<<endl;
}
---------------------------------------------------------------------------------------------------------
[運行結果]
func1:hello world.
func2:haha.
func3 ret:i=1
func4 ret:i=2
funcExt result.
func5 sum:13 a:6 b:7
func6 a:6 c:7 e:9 ext:10
func7 a:6 c:7 ext:10
a++: 7
func8 ext:10
access static ext+1: 11
c: 7
func9 c++: 7
50
a: 7 func11 a++: 7
b: 7 func12 b++: 8

說明:
func1 – lambda基本用法,類型原因需要auto自動推到返回類型
func2 – 沒有特殊用法比如限定符、異常等時,可以忽略圓括號用法
func3 – 指定lambda函數返回值
func4 – 多種不同類型返回值lambda函數時一定需要指定返回類型
func5 – 指定參數列表,以及對原始數據的修改情況,跟一般函數無異
func6 – 按變量複製的方式捕獲scope內變量,需要注意的是,捕獲變量只能是lambda作用域外的自動變量,f在作用域內,不能捕獲、ext屬於靜態變量,本身不能被捕獲,但是在捕獲列表中捕獲報錯,gcc7.x編譯僅僅warning,但是作爲靜態變量,是能夠在lambda函數中直接訪問的(但不是因爲ext靜態變量作用於整個項目,對比func8,雖然自動變量a,c,e在func8作用域之外,但沒有指定捕獲列表是不能直接訪問的)
func7 – ①以全部捕獲的方式對變量進行捕獲 ②lambda默認爲const成員函數,不能對按值傳遞的變量進行修改
func8 – 不指定任何捕獲時,能過訪問靜態變量
func9 – 通過限定符mutable,使按值傳遞的變量能被修改,但因爲是副本原因,能對變量進行修改,但修改對原數據並沒有什麼卵影響
func10、func11 – this捕獲當前對象用法
func12 – 對比一下引用方式捕獲跟按值捕獲的小區別—-按值捕獲在創建lambda時就將變量複製捕獲,造成調用時數據內容產生延遲

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章