zoukankan      html  css  js  c++  java
  • 初探C++11之lambda表达式

    lambda表达式是函数式编程的基础。咱对于函数式编程也没有足够的理解,因此这里不敢胡言乱语,有兴趣的可以自己查找相关资料看下。这里只是介绍C++11中的lambda表达式以及与此相关的闭包(closure)。

    同样,这里首先给出参考文档

    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2927.pdf

    其次,给出两个例子,可以看出lambda表达式的写法

    [](int x, int y) { return x + y; }

    [](int x, int y) -> int { int z = x + y; return z + x; } //表明返回值类型是int型

    也就是说,一个lambda表达式很类似于普通的函数定义的写法,区别在于三点,一是没有函数名(你也可以认为函数名是[]),二是这个函数没有普通函数那样的返回值类型,返回值类型的写法在第2行,即在参数列表与函数定义之间以箭头写明,三是在参数列表之前有一个[] (其实这里还可能有更多的形式,后面会说)

    第三,lambda表达式的标准形式是什么?

    如下。这里先只是列出,后面会详细解释。

    lambda-expression:
      lambda-introducer lambda-declaratoropt compound-statement
    lambda-introducer:
      [ lambda-captureopt ]

    lambda-capture:
      capture-default
      capture-list
      capture-default , capture-list
    capture-default:
      &
      =
    capture-list:
      capture
      capture-list , capture
    capture:
      identifier
      & identifier
      this
    lambda-declarator:
      ( parameter-declaration-clause ) attribute-specifieropt mutableopt exception-specificationopt trailing-return-typeopt


    第四,进一步的分析

    上面的标准形式中,最不好理解的可能就是lambda-capture了。其实它对应的就是闭包的概念,而作用是描述外部传入的参数。

    wiki上有一个例子,如下

    std::vector<int> someList;
    int total = 0;
    std::for_each(someList.begin(), someList.end(), [&total](int x) {
    total += x;
    });
    std::cout << total;

    这段代码做的事情是把someList中的所有变量求和。其中定义的lambda函数如下,

    [&total](int x) {total += x;}

    与前面的例子不同,这里的lambda-introducer是[&total]而不是之前的[]。这么写的作用是,在这个lambda函数中以引用方式使用外部变量total,这样求和结果就可以存放于这个变量中。那么类似的,还可以以传值方式使用外部变量,写成[total]就可以,而且,如果需要用到的外部变量较多,可以有简略的写法。一个完整的列表如下(来自wiki,这里我把他们翻译成我们比较习惯的表述形式)

    [] // 没有定义任何变量。使用未定义变量会导致错误。
    [x, &y] // x 以传值方式传入(默认),y 以引用方式传入。
    [&] // 任何被使用到的外部变量皆隐式地以引用方式加以使用。
    [=] // 任何被使用到的外部变量皆隐式地以传值方式加以使用。
    [&, x] // x 显示地以传值方式加以使用。其余变量以引用方式加以使用。
    [=, &z] // z 显示地以引用方式加以使用。其余变量以传值方式加以使用。


    第五,代码练习及验证

    接下来,我们自己写一些代码来做一些练习以及验证一些事情。以下代码在visual studio 2010下测试通过。

    首先是模仿实例写一些代码。

    #include <iostream>
    #include <algorithm>
    #include <vector>

    void ShowVector(std::vector<int>& vecTest)
    {
    std::for_each(vecTest.begin(), vecTest.end(),
    [](int x)
    {
    std::cout<<x<<' ';
    }
    );
    std::cout<<std::endl;
    }

    int main()
    {
    std::vector<int> vecTest(10,1);

    ShowVector(vecTest);

    std::for_each(vecTest.begin(), vecTest.end(),
    [](int& x)
    {
    x += 2;
    }
    );

    ShowVector(vecTest);

    int iTotal = 0;
    std::for_each(vecTest.begin(), vecTest.end(),
    [&iTotal](int x)
    {
    iTotal += x;
    }
    );
    std::cout << std::endl << iTotal << std::endl;

    iTotal = 0;
    std::for_each(vecTest.begin(), vecTest.end(),
    [&](int x)
    {
    iTotal += x;
    }
    );
    std::cout << std::endl << iTotal << std::endl;

    //iTotal = 0;
    //std::for_each(vecTest.begin(), vecTest.end(),
    // [=](int x)
    // {
    // iTotal += x; // build error,error C3491: 'iTotal': a by-value capture cannot be modified in a non-mutable lambda
    // }
    //);
    //std::cout << std::endl << iTotal << std::endl;

    //iTotal = 0;
    //std::for_each(vecTest.begin(), vecTest.end(),
    // [iTotal](int x)
    // {
    // iTotal += x; // build error, error C3491: 'iTotal': a by-value capture cannot be modified in a non-mutable lambda
    // }
    //);
    //std::cout << std::endl << iTotal << std::endl;

    iTotal = 0;
    std::for_each(vecTest.begin(), vecTest.end(),
    [=, &iTotal](int x)
    {
    iTotal += x;
    }
    );
    std::cout << std::endl << iTotal << std::endl;

    int iAdd = 1;
    std::for_each(vecTest.begin(), vecTest.end(),
    [=](int& x)
    {
    x += iAdd;
    }
    );
    ShowVector(vecTest);

    std::for_each(vecTest.begin(), vecTest.end(),
    [iAdd](int& x)
    {
    x += iAdd;
    }
    );
    ShowVector(vecTest);

    char e = 0;
    scanf_s("%c", &e, 1);
    return 0;
    }

    运行结果如下,

    1 1 1 1 1 1 1 1 1 1
    3 3 3 3 3 3 3 3 3 3

    30

    30

    30
    4 4 4 4 4 4 4 4 4 4
    5 5 5 5 5 5 5 5 5 5

    这里值得注意的是其中的注释掉的代码,这里我们以传值方式使用外部变量,但是在lambda函数内部却试图修改该变量的值,其实这是没什么作用的,而visual studio直接给出报错,检查很仔细,不错。

    那么我们再考虑一个问题,lambda表达式的运行效率?我们来试一下。测试机器双核4G内存。

    #include "windows.h"
    #include <iostream>
    #include <algorithm>
    #include <vector>

    void ModVal(int& iVal)
    {
    iVal *= 3;
    }

    #define ARRAY_SIZE 10000000
    int main()
    {
    int m = 0;
    std::vector<int> vecTest(ARRAY_SIZE,0);
    std::for_each(vecTest.begin(), vecTest.end(),
    [&m](int& x)
    {
    x = m++;
    }
    );

    // test1
    //std::for_each(vecTest.begin(), vecTest.end(),
    // [](int& x)
    // {
    // x *= 3;
    // }
    //);
    // test2
    //for (int i = 0; i < ARRAY_SIZE; i++)
    //{
    // vecTest[i] *= 3;
    //}
    // test3
    std::for_each(vecTest.begin(), vecTest.end(), ModVal);


    char e = 0;
    scanf_s("%c", &e, 1);
    return 0;
    }

    分别对以上的test1, test2和test3使用QueryPerformanceFrequency()函数计时,我们可以得到如下的结果,

    test1: 26.8ms

    test2: 27.6ms

    test3: 24.2ms

    也就是说,最快的是test3,即不采用lambda表达式,而是直接用函数……多少有点出乎意料。而最慢的是直接用for循环的操作,不过它和用lambda表达式的结果很接近。

    第六,其他

    成员函数内部的lambda函数,lambda函数作为对象。留着后面写。

  • 相关阅读:
    Solaris下批量kill掉oracle的session
    我写blog的方式变迁
    filezilla ftp client在win7 下获取ftp目录信息出错解决方法
    GNU System Monitor编译问题
    在vmware的Solaris虚拟机中安装vmtool
    关于golden gate director client的一点点使用总结
    测试 乱弹
    ORM的世界 (再补充)
    Yahoo Konfabulator
    轻量容器和注射依赖 的自实现
  • 原文地址:https://www.cnblogs.com/l00l/p/2338038.html
Copyright © 2011-2022 走看看