共有回帖数 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 回复
Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号
意见反馈 |
关于直线 |
版权声明 |
会员须知