[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:

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
时间: 2024-10-26 19:34:13

[C/C++11语法]_[0基础]_[lamba 表达式介绍]的相关文章

[libcurl]_[0基础]_[使用libcurl下载大文件]

场景: 1. 在Windows编程时, 下载http页面(html,xml)能够使用winhttp库,可是并非非常下载文件,由于会失败. 由此引出了WinINet库,无奈这个库的稳定性比較低,使用样例又少, 下载大文件时常常是不完整,可查找的资料非常少或者是没有特殊情况的解决的方法. 2. 我的原则是假设系统有自带的就用系统的,可是 WinINet 要掌握须要花不少时间. 时间因素考虑到了libcurl. 3. libcurl支持ftp,http等协议的文件读取,还能自己主动获取文件大小, 最重

[C/C++标准库]_[0基础]_[使用fstream合并文本文件]

场景: 1. 就是合并文本文件,而且从第2个文件起不要合并第一行. 2. 多加了一个功能,就是支持2个以上的文件合并. 3. 问题: http://ask.csdn.net/questions/192151 仅仅能说非常easy: 基础只是关吧,这位同学,也有可能不是开发的,放这里也是为了培训基础差的. test.cpp #include <fstream> #include <string> #include <iostream> using namespace st

[wxWidgets]_[0基础]_[经常更新进度条程序]

场景: 1. 非常根据程序的进展需要处理业务,以更新进度条,进度条的目的是为了让用户知道业务流程的进度.一个进度条程序更友好,让用户知道在程序执行.不是没有反应. 2. 现在更新见过这两种方法的进展.事件(信号,队列)的发送让主线程依照发送的顺序来更新进度条,一种是设置一个全局整形变量, 通过执行定时器的方式来更新进度条.第一种不适合在更新频率比較高的地方,比方一秒钟发送了20个事件,这样会造成主线程忙于处理事件界面出现假死状态. 所以最好的办法就是使用第2种通过定时器更新进度条,设置一个合理的

[网络]_[0基础]_[使用putty备份远程数据]

场景: 1. putty是windows上訪问linux服务的免费client之中的一个.用它来ssh到远程server备份数据是常见的做法(在没做好自己主动备份机制前), 通过putty界面尽管也不难.可是每次 反复性的工作总是非常烦人,这时候能够使用putty的命令行工具+批处理写一个自己主动备份下载工具. 写一个批处理文件: backup.bat @echo off @echo 開始备份数据 putty\plink.exe -ssh -l 帐号名 -pw password 192.168.

[ATL/WTL]_[0基础]_[CBitmap复制图片-截取图片-平铺图片]

场景: 1.当你须要截取图片部分区域作为某个控件的背景. 2.须要平铺图片到一个大区域让他自己主动放大时. 3.或者须要合并图片时. 代码: CDC sdc; CDC ddc; sdc.CreateCompatibleDC(NULL); ddc.CreateCompatibleDC(NULL); CBitmap destBmp; destBmp.CreateCompatibleBitmap(CClientDC(NULL),width,height); sdc.SelectBitmap(m_Bit

[C/C++标准库]_[0基础]_[优先队列priority_queue的使用]

std::priority_queue 场景: 1. 对于一个任务队列,任务的优先级由任务的priority属性指明,这时候就须要优先级越高的先运行.而queue并没有排序功能,这时priority_queue是比較好的选择. 2 对于异步的task也是一样.在不断加入新的task时,当然希望优先级越高的先运行. 解析: 1. 假设须要把优先级最高的先pop,那么comp比較时须要返回false. 代码: //1.Elements are popped from the "back"

[C/C++]_[0基础]_[static_cast,reinterpret_cast,dynimic_cast的使用场景和差别]

场景: 1. C++的对象差别于C的原因是他们能够有继承关系, 方法有重载, 覆盖关系等, 他们的对象内存数据结构因此也比較复杂. 2. 非常多情况下我们须要一个父类来存储子类的指针对象进行通用方法的操作.涉及到详细某个子类对象特定操作时又须要强制转换为子类.那么这时候该用什么好呢? 答: 假设不须要类型检查的话就直接用C的强制转换就可以(B*)c. 可是C++ 之父并不推荐使用C的强制类型转换; 原因1是没有编译器检查. 原因2是对象指针在子类父类之间转换时所相应的地址值可能会变化, 这样用C

[wxWidgets]_[0基础]_[不常见但有用的类wxCmdLineParser]

场景: 1. 有时候须要构造命令行字符串传递給函数调用,比方CreateProcess,假设參数是动态的,那么就得使用类似std::vector<string>加入单个參数,之后拼接为一个string. wx有给这类处理类.就是wxCmdLineParser ,它既能拼接为字符串.又能够把字符串分解为一个參数数组. console.cpp #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/wx.h&quo

[zlib]_[0基础]_[使用Zlib完整解压zip内容]

场景: 1. 解压文件一般用在下载了一个zip文件之后解压,或者分析某个文件须要解压的操作上. 2. 解压文件,特别是解压带目录的zip文件往往系统没有提供这类Win32 API,当然C#自带库能解压, 当然这里仅仅讨论C/C++, 像C#和Java这样的开挂的标准库不在考虑范围内. 3. zlib解压文件的使用样例在 contrib\minizip 样例里. 这里基本是直接提取miniunz.c 的代码进行封装解压就可以, 仅仅是改了下支持中文路径. 主文件 zip_util.cpp #inc