签到

06月20日
尚未签到

共有回帖数 1

    人心难测

    等级:
    ambda 表达式语法
    本主题描述了 lambda 表达式的语法,并且提供了一个演示 lambda 表达式结构诸元素的示例。

    以下的程序将 lambda 表达式与 STL 的两个算法 std::generate_n 和 std::for_each 联合使用。前者的 Lambda 表达式用 Fibonacci 数列填充一 std::vectorint 对象,而后者则向控制台打印刚才生成的元素。
    //编译选项:/EHsc
    #include algorithm
    #include iostream
    #include vector const int iElementCount=9; int wmain(int argc, wchar_t **argv, wchar_t **envp){
      std::vectorint vi(iElementCount, 1);
      int iX=1, iY=1;
      std::generate_n(v.begin()+2, iElementCount-2, [=](void)mutable throw()-int{
        int i=iX+iY;
        iX=iY;
        iY=i;
        return i;
      });
      std::for_each(v.begin(), v.end(), [](int i)-void{std::wcoutiL' ';});
      std::wcoutstd::endl;
      std::wcoutiXL' 'iYstd::endl;
      return EXIT_SUCCESS;
    }
    关于 generate_n 和 for_each 算法,参见各自的主题。
    下面的部分描述了 lambda 表达式的语法。

    Lambda 表达式语法
    以下是用 BNF 描述的对 lambda 表达式的形式化定义:
    lambda-expression
      : lambda-introducer lambda-parameter-declaration_opt compount-statement
    lambda-introducer
      : [ lambda-capture_opt ]
    lambda-capture
      : capture-default
      | capture-list
      | capture-default , capture-list
    capture-default
      : &
      | =
    capture-list
      : capture
      | capture-list , capture
    capture
      : identifier
      | & identifier
      | this
    lambda-parameter-declaration
      : (lambda-parameter-declaration-list_opt) mutable-specification_opt exception-specification_opt lambda-return-type-clause_opt
    lambda-parameter-declaration-list
      : lambda-parameter
      | lambda-parameter , lambda-parameter-declaration-list
    lambda-parameter
      : decl-specifier-seq declarator
    lambda-return-type-clause
      : - type-id
    下一节演示了 lambda 表达式的语法。

    Lambda 表达式的各部分
    下图演示了 lambda 表达式的语法。
     (1)  (2)   (3)   (4)  (5)    (6)
      |   |    |    |   |     |
     --- ------- ------- ------- ----- -------------
     [=] (int x) mutable throw() -int {return x*x;}

    1. Lambda 引导(下称“捕捉子句”)lambda-introducer

    2. Lambda 参数表(下称“参数表”)lambda-parameter-declaration-list
    3. 可变说明 mutable-specification
    4. 异常说明 exception-specification
    5. Lambda 返回类型子句(下称“返回类型”)lambda-return-type-clause


    6. 复合语句(下称“表达式体”)
    以下详细描述语法中的各个部分。

    捕捉子句
    Lambda 表达式可以访问与其在同一域中的有着自动存储期的任何变量。捕捉子句指定了 lambda 表达式是用值还是用引用来访问这些变量:有 & 前缀的是引用,没有的是值。空捕捉子句 [] 表示表达式体不访问任何外部的变量。
    “缺省捕捉模式”为没有显示声明的变量指定捕捉模式,其方法是在捕捉子句中把第一个元素设为 & 或者 =。& 说明表达式体可以以引用方式访问所有外部的变量,除非显式指定使用相反的值方式。反之,= 说明表达式体可以以值方式访问所有外部的变量,除非显式指定使用相反的引用方式。例如,如果一个 lambda 表达式体用引用方式访问外部变量 total、并用值方式访问外部变量 factor,那么如下的捕捉子句是等价的:
    [&total, factor]
    [&, factor]
    [=, &total]
    可以在类的成员函数中使用 lambda 表达式。在捕捉子句中使用 this 指针以提供访问类成员的途径。关于如何在成员函数中使用 lambda 表达式,参见“Lambda 表达式示例”主题中的“示例:在成员函数中使用 Lambda 表达式”一节。

    参数表
    Lambda 表达式的参数表与函数的参数表相似,但有下列例外:
    * 不能有缺省参数
    * 不能有变长参数列表
    * 不能有无名参数
    Lambda 表达式也可以接受另一个 lambda 表达式作为其参数。更多信息,参见“Lambda 表达式示例”主题中的“高阶 Lambda 表达式”一节。
    参数表是可选的。如果不提供可变说明、异常说明和返回类型子句,参数表就可以省略。以下的示例演示了如何省略参数表:
    int x=4, y=5;
    int z=[=]{return x+y;}();

    可变说明
    可变说明部分允许 lambda 表达式体更改以值方式捕捉的变量。前面的例子使用了 mutable 关键字,因此 lambda 表达式体可以更改它通过值方式捕捉到的外部变量 x 和 y 的副本。但因为这两个变量是以值方式捕捉的,所以它们在调用 generate_n 之后仍然是原值 1。

    异常说明
    可以使用 throw() 异常说明来指定 lambda 表达式不抛出任何异常。与普通的函数一样,Visual C++ 编译器在一个已经声明了 throw() 的 lambda 表达式中抛出异常时产生 C4297 警告,如下例所示:
    [](void)throw()-void{throw 5;}();
    更多信息,参见“异常说明”主题。

    返回类型
    Lambda 表达式的返回类型子句与普通函数或方法的相似。但是,返回类型子句必须用 - 接在参数表之后。
    如果 lambda 表达式体中有且只有一条语句、且这条语句是 return,或者根本不返回值,那么返回类型子句可以省略。在前一种情况下,编译器从该 return 语句来推导返回类型;否则,编译器认为返回类型是 void。
    Lambda 表达式也可以产生另一个 lambda 表达式来作为它的返回值。更多信息,参见“Lambda 表达式示例”主题中的“高阶 Lambda 表达式”一节。

    表达式体
    Lambda 表达式体可以包含可在任何普通函数或方法中出现的任何语句。与普通函数的函数体相同,在 lambda 表达式体中可以访问:
    * 参数
    * 局部定义的变量
    * 类成员变量(如果在类中定义)
    * 任何有静态存储期的变量(例如,全局变量)
    另外,lambda 表达式体可以访问捕捉到的变量。一个变量如果在捕捉子句中出现,则称它为被“显式捕捉”的;否则,称它为被“隐式捕捉”的。
    参数表的例子中,x 和 y 是被隐式捕捉的。下例演示了用值方式显式捕捉 n、用引用方式隐式捕捉 m:
    int m=0, n=0;
    [&, n](int a)mutable-void{m=++n+a;}(4);
    std::wcoutmstd::endlnstd::endl;
    此例产生如下的控制台输出:
    5
    0
    因为 n 是以值方式捕捉的,lambda 表达式执行完之后其值仍为 0。
    虽然 lambda 表达式只能捕捉有自动存储期的变量,但是在表达式体内仍然可以使用有静态存储期的变量。下例联合使用 generate 函数和一个 lambda 表达式以向一个 vetor 中填充元素,其中的 lambda 表达式修改了一个静态变量。
    //编译选项:/c /EHsc
    #include vector
    #include algorithm void FillVector(std::vectorint &vi){
      static int s_iNext=1;
      std::generate(vi.begin(), vi.end(), []{return s_iNext++});
    }
    更多信息,参见“generate”主题。

    参见
    参考
     C++ 中的 Lambda 表达式
     Lambda 表达式示例
     generate
     generate_n
     for_each
     异常说明
     编译器警告(级别 1)

    楼主 2016-06-23 12:34 回复

    人心难测

    等级:
    Lambda 表达式示例
    本主题演示了如何在程序中使用 lambda 表达式。关于 lambda 表达式的概述,参见“C++ 中的 Lambda 表达式”主题。关于 lambda 表达式的结构,参见“Lambda 表达式语法”主题。

    本主题中
    示例:声明 Lambda 表达式
    示例:调用 Lambda 表达式
    示例:嵌套 Lambda 表达式
    示例:高阶 Lambda 表达式
    示例:在成员函数中使用 Lambda 表达式
    示例:在模板中使用 Lambda 表达式
    示例:异常处理
    示例:在 Lambda 表达式中使用托管类型示例:声明 Lambda 表达式
    描述:
    因为 lambda 表达式是具有类型的,所以可以将其赋给一个 auto 变量或一个 std::function 对象,如下例所示:
    代码:
    #include functional

    auto f1=[](int x, int y){return x+y;};
    functionint(int, int) f2=[](int x, int y){return x*y;};
    备注:
    更多关于 auto 关键字的信息,参见“auto 关键字(类型推导)”主题。更多关于 function 类的信息,参见“function 类”主题
    描述:
    Visual C++ 编译器在 lambda 表达式被声明而非被调用时将其与所捕捉到的变量进行绑定。下例显示了一个值捕捉 i、引用捕捉 j 的 lambda 表达式。因为 i 是被值捕捉的,程序后面对 i 的更改不影响表达式的结果;但是因为 j 是被引用捕捉的,程序后面对 j 的更改会影响表达式的结果。
    代码:
    //编译选项:/EHsc
    #include iostream
    #include functional

    int wmain(int argc, wchar_t **argv, wchar_t **envp){
      int i=3, j=5;
      std::functionint(void) f=[i, &j]{return i+j;};
      i=22;
      j=44;
      std::wcoutf()std::endl;
      return EXIT_SUCCESS;
    }
    输出:
    47

    示例:调用 Lambda 表达式
    可以直接调用 lambda 表达式,或者将其作为参数传递给诸如 std::find_if 的 STL 算法。
    描述:
    下例声明了一个计算两数之和的 lambda 表达式,并且立即使用参数 5 和 4 来调用它:
    代码:
    int n=[](int x, int y){return x+y;}(5, 4);
    std::wcoutnstd::endl;
    输出:
    9

    描述:
    下例将一个判断参数奇偶性的 lambda 表达式作为参数传递给 find_if 函数:
    代码:
    //编译选项:/EHsc
    #include list
    #include algorithm
    #include iostream

    int wmain(int argc, wchar_t **argv, wchar_t **envp){
      std::listint li;
      li.push_back(13);
      li.push_back(17);
      li.push_back(42);
      li.push_back(46);
      li.push_back(99);
      const listint::const_iterator cit=std::find_if(li.begin(), li.end(), [](int i)-bool{
        return !(i%2);
      });
      if(cit==li.end()){
        std::wcoutL"The list contains no even numbers."std::endl;
      }else{
        std::wcoutL"The first even number in the list is "*citL"."std::endl;
      }
      return EXIT_SUCCESS;
    }
    输出:
    The first even number in the list is 42.
    备注:
    更多信息,参见主题“find_if”和“algorithm”。

    描述:
    可以直接使用函数调用操作符 operator() 来调用已经被赋给另一变量的 lambda 表达式。下例将一个 lambda 表达式赋给一 auto 变量,然后使用函数调用操作符来调用该 lambda 表达式:
    代码:
    auto f=[](int x, int y){return x+y;};
    std::wcoutf(21, 12)std::endl;
    输出:
    33
    备注:
    更多信息,参见“函数调用(C++)”主题。

    示例:嵌套 Lambda 表达式
    描述:
    可以在一个 lambda 表达式中嵌套另一个。下例演示了一个包含另一个 lambda 表达式的 lambda 表达式,其中内层返回其参数的 2 倍,而外层使用它的参数调用内层表达式然后将结果加 3。
    代码:
    std::wcout[](int x){return [](int y){return y*2;}(x)+3;}(5)std::endl;
    输出:
    13

    示例:高阶 Lambda 表达式
    描述:
    许多编程语言支持“高阶函数”的概念。在 C++ 中,“高阶函数”可以是一个接受其它 lambda 表达式作为参数的、或者返回另一个 lambda 表达式的 lambda 表达式。可以使用 std::function 类来让 C++ lambda 表达式表现得像一个高阶函数。下例演示了一个返回 std::function 对象以及接受一个 std::function 对象作为参数的两个 lambda 表达式。
    代码:
    #include iostream
    #include functional

    auto g=[](int x)-std::functionint(int){

     return [=](int y){
        return x+y;
      };
    };
    auto h=[](const std::functionint(int) &f, int z){
      return f(z)+1;
    };
    std::wcouth(g(7), 8)std::endl;
    输出:
    16

    示例:在成员函数中使用 Lambda 表达式
    描述:
    可以在成员函数中使用 lambda 表达式,其中可以访问所在类中的任何成员。显式或隐式捕捉 this 指针以访问所在类的成员。
    下例演示了一个封装了放大因子 iScale 的 CScale 类,其 ApplyScale 成员函数使用一个 lambda 表达式以打印给定 vector 对象中的诸元素与该放大因子的乘积。Lambda 表达式中显式捕捉了 this 指针以便访问 iScale 成员。
    代码:
    //编译选项:/EHsc
    #include algorithm
    #include iostream
    #include vector

    class CScale{
    public:
      explicit CScale(int iScale){
        this-iScale=iScale;
      }
      void ApplyScale(const std::vectorint &vi)const{
        std::for_each(v.begin(), v.end(), [this](int i)-void{
          std::wcouti*iScalestd::endl;
        });
      }
    private:
      int iScale;
    };

    int wmain(int argc, wchar_t **argv, wchar_t **envp){
      std::vectorint vi;
      vi.push_back(3);
      vi.push_back(6);
      vi.push_back(9);
      CScale s(3);
      s.ApplyScale(vi);
      return EXIT_SUCCESS;
    }
    输出:
    9
    18
    27
    备注:
    可以显式使用 this 指针,如:
      void ApplyScale(const std::vectorint &vi)const{
        std::for_each(v.begin(), v.end(), [this](int i){
          std::wcouti*this-iScalestd::endl;
        });
      }
    也可以隐式捕捉 this 指针,如:
      void ApplyScale(const std::vectorint &vi)const{
        std::for_each(v.begin(), v.end(), [=](int i){
          std::wcouti*iScalestd::endl;
        });
      }

    示例:在模板中使用 Lambda 表达式
    描述:
    因为 lambda 表达式是具有类型的,可以将它们与 C++ 模板一起使用。下例演示了 NegateAll 和 PrintAll 函数,前者对 vector 对象中的每一元素应用一元取负操作符,后者将它们打印至控制台。
    代码:
    //编译选项:/EHsc
    #include algorithm
    #include iostream
    #include vector

    templatetypename TData
    void NegateAll(std::vectorTData &v){
      std::for_each(v.begin(), v.end(), [](TData &n){n=-n;});
    }
    templatetypename TData
    void PrintAll(const std::vectorTData &v){
      std::for_each(v.begin(), v.end(), [](const TData &n){std::wcoutnstd::endl;});
    }
    int wmain(int argc, wchar_t **argv, wchar_t **envp){
      std::vectorint vi;
      vi.push_back(34);
      vi.push_back(-43);
      vi.push_back(56);
      NegateAll(vi);
      PrintAll(vi);
      return EXIT_SUCCESS;
    }
    输出:
    -34
    43
    -56
    备注:
    更多信息,参见“模板”主题。

    示例:异常处理
    描述:
    Lambda 表达式体遵循结构化异常处理(SEH)以及 C++ 异常处理的规则。可以在表达式体中处理已抛出的异常,也可以留给外围处理。下例使用 for_each 函数和一个 lambda 表达式向一个 vector 对象中填充另一个的值,并使用 try/catch 块来处理对第一个 vector 的非法访问。
    代码:
    //编译选项:/EHsc
    #include algorithm
    #include iostream
    #include vector

    int wmain(int argc, wchar_t **argv, wchar_t **envp){
      std::vectorint viElements(3), viIndices(3);
      viIndices[0]=0;
      viIndices[1]=-1;//下标越界异常
      viIndices[2]=2;
      try{
        std::for_each(viIndices.begin(), viIndices.end(), [&](int iIndex){
          viElements.at(iIndex)=iIndex;
        });
      }catch(const std::out_of_range &ex){
        std::cerr"Caught '"ex.what()"'."std::endl;
      }
      return EXIT_SUCCESS;
    }
    输出:
    Caught 'invalid vectorT subscript'.
    备注:
    更多信息,参见“Visual C++ 中的异常处理”主题。

    示例:在 Lambda 表达式中使用托管类型
    描述:
    捕捉子句中不能出现托管类型的变量。但是,可以向 lambda 表达式传递托管类型的参数。下例演示了一个值捕捉局部非托管变量 ch、接受一个 System::String 对象作为参数的 lambda 表达式:
    代码:
    //编译选项:/clr
    char ch='!';
    [=](System::String ^s){System::Console::WriteLine(s+System::Convert::ToChar(ch));}("Hello");
    输出:
    Hello!
    备注:
    可以将 lambda 表达式与 STL/CLR 库一起使用。更多信息,参见“STL/CLR 库参考”主题。

    参见
    参考
     C++ 中的 Lambda 表达式
     Lambda 表达式语法
     auto 关键字(类型推导)
     function 类
     find_if
     algorithm
     函数调用(C++)
     Visual C++ 中的异常处理
    其它资源
     模板
     STL/CLR 库参考

    1楼 2016-06-23 12:35 回复

共有回帖数 1
  • 回 帖
  • 表情 图片 视频
  • 发表

登录直线网账号

Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号 意见反馈 | 关于直线 | 版权声明 | 会员须知