程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> [C/C++11語法]_[初級]_[lamba 表達式介紹]

[C/C++11語法]_[初級]_[lamba 表達式介紹]

編輯:C++入門知識

[C/C++11語法]_[初級]_[lamba 表達式介紹]


場景

lambda 表達式在很多語言裡都有一席之地,因為它的原因,可以在函數裡快速定義一個便攜的函數,或者在函數參數裡直接快速構造和傳遞. 它可以說是匿名函數對象,一般只適用於某個函數內,只做臨時使用. 一般是需要在對某個數據臨時特殊處理時使用,比如對某種參數類型進行限定的再次封裝和行為約束.

說明

lambda 語法.
圖1:
microsoft的圖片

Capture Clause(捕抓條款)組合:

規則1:

[] : 空捕抓條款,表明 lambda body 不訪問閉合范圍(enclosing scope)的任何變量.
[&] : 以引用的方式訪問閉合范圍內的前面已聲明變量.
[=] : 以值的方式訪問閉合范圍內的前面已聲明的變量.
[this] : 訪問類實例的this指針.

規則2

&,=,this 默認類型不能同時聲明 相同類型的捕抓不能和默認類型同時聲明,比如[&,&i] // 編譯錯誤 不相同類型的非默認類型可以同時聲明.比如[&i,j] 對同一個變量不能捕抓多次或者同時以不同捕抓方式聲明. [&i,&i] [&i,i]

Parameter List(參數列表)

和捕抓列表不一樣,lambda可以輸入參數,一般情況下參數是為了和 C++ 函數轉換才需要. 也可以使用 lambda 表達式作為參數. 在C++14裡, 如果使用的是泛型參數,那麼你可以使用 auto 聲明.
auto y = [] (auto first, auto second)
{
    return first + second;
};

Mutable Specification(Mutable關鍵字)

可以使用mutable來修改捕抓條款裡聲明的傳值變量, 注意只是相當於聲明了一個本地的mutable變量作為臨時變量而已,並不會修改enclosing scope 變量范圍的值. 看 例子1

Exception Specification(異常規范)

可以使用throw()來聲明這個lambda 不拋出C++異常. 但是在C++11裡這種使用方式已經被廢棄.

Return Type(返回類型)

vs2010 必須聲明返回類型. gcc 可以不聲明返回類型,但是body 裡必須有能推導的 return 表達式類型.

其他

參考C++14 lambda Expression 的說明.

lambda 和 C++普通函數的轉換.

根據C++14 lambda表達式條款6, lambda 可以轉換為C++函數, 但是必須滿足以下的轉化條件,而且只能轉換為閉包類型自帶的特定類型的函數, 閉包類型自帶了一個函數指針?.
The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-
explicit const conversion function to pointer to function with C ++ language linkage (7.5) having the same
parameter and return types as the closure type’s function call operator.

– 轉換前的 lambda 條件:
1. 非泛型.
2. 沒有捕抓列表(即沒有捕抓任何變量)

– 轉換後的 函數
1. 同參數.
2. 相同返回類型.
3. 非虛擬
4. 非顯式常量.(non-explicit const)

例子

例子1

lambda 在STL裡的使用場景. 由於vs2010 並不支持lambda 到 C++ 函數的轉換,所以並不能通過編譯. mutable 的作用.

vs2010

#include "stdafx.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class B
{
public:
    B(int value):two("B")
    {
        one = value;
        std::cout << "B" << std::endl;
    }
    ~B(){two.clear(); std::cout << "~B" << std::endl;}
    int one;
    std::string two;
};

void TestSort()
{
    std::cout << "TestSort" << std::endl;
    // 2010也不支持快速枚舉. for(B* b: bs)
    // 創建10個對象

    std::vector bs(10);
    int value = 0;
    std::generate(bs.begin(),bs.end(),[&value]()->B*
    {
        B* b = new B(++value);
        return b;
    });

    // 搜索奇數的對象
    std::vector bs2;
    std::for_each(bs.begin(),bs.end(),[&bs2](B* b)
    {
        if(b->one % 2)
        {
            bs2.push_back(b);
        }
    });

    // 排序之前是升序.
    std::cout << "Before Sort ==" << std::endl;
    std::for_each(bs2.begin(),bs2.end(),[](B* b)
    {
        std::cout << b->one << std::endl;
    });

    // 降序排列
    std::cout << "After Sort ==" << std::endl;
    std::sort(bs2.begin(),bs2.end(),[](B* first,B* second)
    {
        return first->one > second->one;
    });

    std::for_each(bs2.begin(),bs2.end(),[](B* b)
    {
        std::cout << b->one << std::endl;
    });
}

typedef void (*FUNC)();
void Foo(FUNC func)
{
    func();
}

void TestLambdaAsync()
{
    std::cout << "TestLambdaAsync ==" << std::endl;
    //2010 不支持lambda轉換為FUNC,它只能用於template裡的實現;需要vs2012以上才支持.vs2010支持lambda到FUNC的轉換.
    //     這樣就可以直接在 CreateThread裡使用 lambda.
    //g++ 4.8.1 可以.
    // Foo([](){std::cout << "lambda" << std::endl;});
    // 錯誤   2   error C2664: “Foo”: 不能將參數 1 從“`anonymous-namespace'::”轉換為“FUNC”
}


void TestMutable()
{
   std::cout << "TestMutable==========" << std::endl;
   int m = 0;
   int n = 0;


   //去掉mutable會出現編譯錯誤.Error:表達式必須是可以修改的左值.
   // mutable 作用之一就是省略掉本地變量的定義.
   // [&, n] (int a){ int n1 = n; m = ++n1 + a; }(4);

   [&, n] (int a)mutable{m = ++n + a; }(4);
   std::cout << m << std::endl << n << std::endl;
}

class Base
{
public:
  virtual ~Base() {}
  virtual int call( float ) =0;
};

template< typename T>
class Eraser : public Base
{
public:
   Eraser( T t ) : m_t(t) { }
   int call( float f ) { return m_t(f); }
private:
   T m_t;
};

class Erased
{
public:
   template
   Erased( T t ) : m_erased( new Eraser(t) ) { }

   int do_call( float f )
   {
      return m_erased->call( f );
   }
private:
   Base* m_erased;
};

template
class A1
{
public:
    A1(FUNC func):func_(func){}
    void Run()
    {
        func_();
    }
    FUNC func_;
private:
};

Erased* GetErased()
{
    int i = 9;
    Erased *e_useful = new Erased( [i]( float f ) mutable ->int
    { 
        std::cout << ++i << std::endl;
        return 42; 
    } );
    return e_useful;
}

int main(int argc, char const *argv[])
{
    TestSort();
    TestMutable();

    int i = 0;
    auto func1 = [i]()mutable
    {
        std::cout << "A: " << ++i << std::endl;
    };
    A1 a(func1);
    a.Run();

    Erased* e_useful = GetErased();
    e_useful->do_call(9);

    return 0;
}

輸出:

TestSort
B
B
B
B
B
B
B
B
B
B
Before Sort ==
1
3
5
7
9
After Sort ==
9
7
5
3
1
TestMutable==========
5
0
A: 1
10

例子2

使用了lambda 作為 pthread 的回調函數. 多線程下使用 shared_ptr 的方法.

gcc 4.8.1

// function_lambda_expression.cpp
// compile with: /EHsc /W4
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "pthread.h"

class A
{
public:
    A()
    {
        std::cout << "A" << std::endl;
        buf_ = (char*)malloc(6);
        strcpy(buf_,"hello");
    }
    ~A()
    {
        free(buf_);
        buf_ = NULL;
        std::cout << "~A" << std::endl;
    }
    char* buf_;
    /* data */
};

// g++ 4.8.1 支持lambda函數到普通函數的轉換,但是有條件,不支持capture(推理)
// 查看C++14規范第6條款關於lambda表達式和普通C++函數的轉換關系.
// 傳遞共享指針,多線程共享變量例子.
void TestLambdaAsync(std::shared_ptr& a1)
{
    std::cout << "Begin a1.use_count: " << a1.use_count() << std::endl;
    pthread_t t1;
    std::shared_ptr* a = new std::shared_ptr(a1);
    std::cout << "After a1.use_count: " << a1.use_count() << std::endl;

    // 如果是C函數指針作為參數,那麼lambda也不能捕抓任何變量,如[&a],不然會報錯.
    // error: cannot convert 'TestLambdaAsync()::__lambda0' to 'void* (*)(void*)' for argument '3' to 'int pthread_create(pthread_t*, pthread_attr_t_* const*, void* (*)(void*), void*)'},NULL);
    pthread_create(&t1,NULL,[](void* data)->void*
    {
        std::shared_ptr* a = reinterpret_cast

輸出:

Start ==
A
Begin a1.use_count: 1
After a1.use_count: 2
Begin a1.use_count: 2
After a1.use_count: 3
Begin a1.use_count: 3
After a1.use_count: 4
pthread_create: hello
Begin a1.use_count: 3
After a1.use_count: 4
pthread_create: hello
pthread_create: hello
Begin a1.use_count: 2
After a1.use_count: 3
pthread_create: hello
Begin a1.use_count: 3
After a1.use_count: 3
Begin a1.use_count: 3
After a1.use_count: 4
pthread_create: hello
Begin a1.use_count: 3
After a1.use_count: 4
pthread_create: hello
Begin a1.use_count: 3
After a1.use_count: 4
pthread_create: hello
pthread_create: hello
Begin a1.use_count: 2
After a1.use_count: 3
pthread_create: hello
Sleep
pthread_create: hello
Exit ==
~A

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved