zoukankan      html  css  js  c++  java
  • [C/C++11语法]_[0基础]_[lamba 表达式介绍]

    场景

    1. lambda 表达式在非常多语言里都有一席之地,由于它的原因,能够在函数里高速定义一个便携的函数,或者在函数參数里直接高速构造和传递.
    2. 它能够说是匿名函数对象,一般仅仅适用于某个函数内,仅仅做暂时使用.
    3. 通常是须要在对某个数据暂时特殊处理时使用,比方对某种參数类型进行限定的再次封装和行为约束.

    參考

    1. C# Lambda表达式及其优势
    2. Lambda Expressions in C++
    3. Exception Specifications (throw) (C++)
    4. noexcept (C++)
    5. what-is-the-lifetime-of-a-c-lambda-expression

    说明

    1. lambda 语法.
      图1:
      microsoft的图片

    Capture Clause(捕抓条款)组合:

    规则1:

    [] : 空捕抓条款,表明 lambda body 不訪问闭合范围(enclosing scope)的不论什么变量.
    [&] : 以引用的方式訪问闭合范围内的前面已声明变量.
    [=] : 以值的方式訪问闭合范围内的前面已声明的变量.
    [this] : 訪问类实例的this指针.

    规则2

    • &,=,this 默认类型不能同一时候声明
    • 同样类型的捕抓不能和默认类型同一时候声明,比方[&,&i] // 编译错误
    • 不同样类型的非默认类型能够同一时候声明.比方[&i,j]
    • 对同一个变量不能捕抓多次或者同一时候以不同捕抓方式声明. [&i,&i] [&i,i]

    Parameter List(參数列表)

    1. 和捕抓列表不一样,lambda能够输入參数,普通情况下參数是为了和 C++ 函数转换才须要.
    2. 也能够使用 lambda 表达式作为參数.
    3. 在C++14里, 假设使用的是泛型參数,那么你能够使用 auto 声明.
    auto y = [] (auto first, auto second)
    {
        return first + second;
    };
    

    Mutable Specification(Mutable关键字)

    1. 能够使用mutable来改动捕抓条款里声明的传值变量, 注意仅仅是相当于声明了一个本地的mutable变量作为暂时变量而已,并不会改动enclosing scope 变量范围的值. 看 样例1

    Exception Specification(异常规范)

    1. 能够使用throw()来声明这个lambda 不抛出C++异常. 可是在C++11里这样的使用方式已经被废弃.

    Return Type(返回类型)

    1. vs2010 必须声明返回类型.
    2. gcc 能够不声明返回类型,可是body 里必须有能推导的 return 表达式类型.

    其它

    1. 參考C++14 lambda Expression 的说明.

    lambda 和 C++普通函数的转换.

    1. 依据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

    1. lambda 在STL里的使用场景.
    2. 由于vs2010 并不支持lambda 到 C++ 函数的转换,所以并不能通过编译.
    3. mutable 的作用.

    vs2010

    #include "stdafx.h"
    #include <memory>
    #include <Windows.h>
    #include <stdlib.h>
    #include <algorithm>
    #include <iostream>
    #include <vector>
    #include <string>
    #include <regex>
    
    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<B*> bs(10);
        int value = 0;
        std::generate(bs.begin(),bs.end(),[&value]()->B*
        {
            B* b = new B(++value);
            return b;
        });
    
        // 搜索奇数的对象
        std::vector<B*> 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'::<lambda6>”转换为“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<typename T>
       Erased( T t ) : m_erased( new Eraser<T>(t) ) { }
    
       int do_call( float f )
       {
          return m_erased->call( f );
       }
    private:
       Base* m_erased;
    };
    
    template<typename FUNC>
    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<decltype(func1)> 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

    1. 使用了lambda 作为 pthread 的回调函数.
    2. 多线程下使用 shared_ptr 的方法.

    gcc 4.8.1

    // function_lambda_expression.cpp
    // compile with: /EHsc /W4
    #include <Windows.h>
    #include <algorithm>
    #include <iostream>
    #include <vector>
    #include <memory>
    #include <string>
    #include <string.h>
    #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<A>& a1)
    {
        std::cout << "Begin a1.use_count: " << a1.use_count() << std::endl;
        pthread_t t1;
        std::shared_ptr<A>* a = new std::shared_ptr<A>(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>* a = reinterpret_cast<std::shared_ptr<A>*>(data);
            std::cout << "pthread_create: " << (*a)->buf_ << std::endl;
    
            delete a;
            return NULL;
        },a);
    }
    
    int main()
    {
        std::cout << "Start ==" << std::endl;
        std::shared_ptr<A> a(new A());
    
        for (int i = 0; i < 10; ++i)
        {
            TestLambdaAsync(a);
        }
    
    
    
        while(a.use_count() > 1)
        {
            std::cout << "Sleep" << std::endl;
            Sleep(1);
        }
    
        std::cout << "Exit ==" << std::endl;
    }
    

    输出:

    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
    
  • 相关阅读:
    实验六 继承定义与使用
    第四周java实验
    解决 GitHub 提交次数过多 .git 文件过大的问题
    添加开机启动项目
    bash启用 z(同理git bash)
    WIndows to go安装win10系统到移动硬盘
    Make for Windows
    zotero引用3GPP标准暂不完善——使用BibTeX
    Spyder中内嵌的IPython Console自动续行而不运行的问题
    texstudio.org打不开——下载最新版TeXstudio
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/7270057.html
Copyright © 2011-2022 走看看