• C++教程之lambda表达式一


    什么是Lambda?

    C++ 11增加了一个很重要的特性——Lambda表达式。营里(戴维营)的兄弟都对Objective-C很熟悉,很多人多block情有独钟,将各种回调函数、代理通通都用它来实现。甚至有人选择用FBKVOController、BlocksKit等开源框架将KVO、控件事件处理都改为通过block解决。原因就是简单、方便、直观,函数的定义和使用出如今同一个地方。这里的Lambda表达式实际上和block很类似,当然假设你用它和Swift语言的闭包比較,那就是一回事了。以下先看几个Lambda和block的演示样例代码。

    1 .Objective-C的block演示样例代码,使用^表示block类型,整体来说与函数指针的定义类似。

    #import <Foundation/Foundation.h>
    
    int main()
    {
            void (^block)() = ^void() {
                    NSLog(@"In block");
            };
    
            block();
            return 0;
    }
    

    编译执行:

    $ clang main.m -framework Foundation
    $ ./a.out 
    2015-01-28 14:17:52.763 a.out[9901:165707] In block
    

    2 .Swift的闭包,參数列表、返回值类型等都写在花括号{}内部。

    let closure = {
            () -> Void in
            println("In swift")
    }
    
    closure()
    

    编译执行:

    $ swiftc main.swift 
    $ ./main 
    In swift
    

    測试Swift也能够直接使用Playground或REPL(Read-Eval-Print-Loop)环境。

    3 .C++的Lambda表达式。

    #include <iostream>
    
    int main()
    {
            auto lambda = []() -> void{
                    std::cout << "In lambda" << std::endl;
            };
            lambda();
            return 0;
    }
    

    编译执行,注意使用C++ 11的特性进行编译:

    $ clang++ main.cpp -std=c++11
    $ ./a.out 
    In lambda
    

    Lambda、block实际上都是一个闭包(closure),它们都类似于一个匿名的函数,可是拥有捕获所在作用域中变量的能力;可以将函数做为对象一样使用。通经常使用它们来实现回调函数、代理等功能。

    Lambda用法

    1 .基本形式

    语法 序号
    [ 捕获列表 ] ( 形參数列表 ) mutable(可选) 异常属性 -> 返回值类型 { 函数体 } (1)
    [ capture-list ] ( params ) -> ret { body } (2)
    [ capture-list ] ( params ) { body } (3)
    [ capture-list ] { body } (4)
    • (1)为完整的形式,包括变量捕获列表、形參列表、可变属性(可选)和返回值类型等。
    • (2)省略了mutable,表示Lambda不能改动捕获的变量。
    • Lambda的返回值类型假设能够由函数体中的实际返回值推导出,能够省略。
    • 假设没有形參,能够省略圆括号。

    2 .捕获列表

    lambda表达式中能够获取(捕获)它所在作用域中的变量值,而且有两种捕获方式:引用和值。我们能够在捕获列表中设置各变量的捕获方式。假设没有设置捕获列表,lambda默认不能捕获不论什么的变量,这点与block不同。

    #include <iostream>
    
    int main()
    {
            int a = 123;
    
            auto lambda = []()->void{
                    std::cout << "In lambda: " << a << std::endl;
            };
            lambda();
    
            return 0;
    }
    

    编译执行:

    $ clang++ main.cpp -std=c++11
    main.cpp:8:33: error: variable 'a' cannot be implicitly captured in a lambda
          with no capture-default specified
                    std::cout << "In lambda: " << a << std::endl;
                                                  ^
    main.cpp:5:6: note: 'a' declared here
            int a = 123;
                ^
    main.cpp:7:16: note: lambda expression begins here
            auto lambda = []()->void{
                          ^
    1 error generated.
    

    []中设置捕获列表,就能够在lambda中使用变量a了,这里使用按值(=, by value)捕获。

    #include <iostream>
    
    int main()
    {
            int a = 123;
    
            auto lambda = [=]()->void{
                    std::cout << "In lambda: " << a << std::endl;
            };
            lambda();
    
            return 0;
    }
    

    编译执行:

    $ clang++ main.cpp -std=c++11
    $ ./a.out 
    In lambda: 123
    

    捕获列表的设置方式:

    |设置方式|结果| |-|-| |[]|不捕获| |[=]|所有按值捕获| |[&]|所有按引用捕获| |[a, b]|按值捕获变量a和b| |[&a, b]|按引用捕获a,按值捕获b| |[&, a]|按值捕获a,其他变量所有按引用捕获| |[=, &a]|按引用捕获a,其他所有按值捕获|

    注意: 捕获列表没有先后顺序;捕获列表中的变量不能出现反复申明,比方[&, &a]&中已经包括了&a的申明了,因此不能再出现&a

    详细的捕获类型,能够通过打印变量地址进行查看。

    • 按值捕获:
    auto lambda = [=]()->void{
        std::cout << "In lambda: " << &a << std::endl;
    };
    

    执行结果为:

    $ clang++ main.cpp -std=c++11
    $ ./a.out 
    0x7fff555c69b8
    In lambda: 0x7fff555c69b0
    
    • 按引用捕获:
    auto lambda = [&]()->void{
        std::cout << "In lambda: " << &a << std::endl;
    };
    

    执行结果为:

    $ clang++ main.cpp -std=c++11
    $ ./a.out 
    0x7fff58a9b9b8
    In lambda: 0x7fff58a9b9b8
    

    3 .可变类型(mutable)

    按值传递到lambda中的变量,默认是不可变的(immutable),假设须要在lambda中进行改动的话,须要在形參列表后加入mutablekeyword(按值传递无法改变lambda外变量的值)。

    #include <iostream>
    
    int main()
    {
            int a = 123;
            std::cout << a << std::endl;
    
            auto lambda = [=]() mutable ->void{
                    a = 234;
                    std::cout << "In lambda: " << a << std::endl;
            };
            lambda();
    
            std::cout << a << std::endl;
    
            return 0;
    }
    

    编译执行结果为:

    $ clang++ main.cpp -std=c++11
    lishan:c_study apple$ ./a.out 
    123
    In lambda: 234  #能够改动
    123             #注意这里的值,并没有改变
    

    假设没有加入mutable,则编译出错:

    $ clang++ main.cpp -std=c++11
    main.cpp:9:5: error: cannot assign to a variable captured by copy in a
          non-mutable lambda
                    a = 234;
                    ~ ^
    1 error generated.
    

    4 .返回值类型。

    lambda的返回值类型能够省略,编译器会依据实际返回值的类型自己主动推导。

    #include <iostream>
    
    int main()
    {
            int a = 123;
            std::cout << a << std::endl;
    
            auto lambda = [=]() mutable{
                    a = 234;
                    std::cout << "In lambda: " << a << std::endl;
                    return a;
            };
            int b = lambda();
    
            std::cout << b << std::endl;
    
            return 0;
    }
    

    编译执行:

    $ clang++ main.cpp -std=c++11
    $ ./a.out 
    123
    In lambda: 234
    234
    

    參考资料

    1. https://msdn.microsoft.com/en-us/library/dd293603.aspx
    2. http://www.oracle.com/technetwork/articles/servers-storage-dev/howto-use-lambda-exp-cpp11-2189895.html
    3. http://en.cppreference.com/w/cpp/language/lambda
    4. http://www.cprogramming.com/c++11/c++11-lambda-closures.html

    本文档由长沙戴维营教育整理。

  • 相关阅读:
    c++ 判断是元音还是辅音
    c++示例 计算器
    c++容器 算法 迭代
    获取 Django版本号的两种方式
    ImportError: No module named pytz
    Nginx配置proxy_pass转发的/路径问题
    VIM复制粘贴大全!
    SSL协议与数字证书原理
    分析支付宝客户端的插件机制
    PHP session的实现原理
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4506217.html
Copyright © 2020-2023  润新知