概述
C++ 11 & 14 中的Lambda表达式 补充
auto func = [capture] (params) opt -> ret { func_body; };
[捕获列表] (形参列表) mutable 异常列表-> 返回类型{ 函数体 }
捕获列表:定义lambda函数所在的作用域中的指定变量可以在lambda函数中使用
其中func是可以当作lambda表达式的名字,作为一个函数使用,capture是捕获列表,params是参数表,opt是函数选项(mutable之类), ret是返回值类型,func_body是函数体。
各项的含义:
捕获列表:捕获外部变量,捕获的变量可以在函数体中使用,可以省略,即不捕获外部变量。
形参列表:和普通函数的形参列表一样。可省略,即无参数列表
值捕获得到的变量为只读变量,如果要修改,需要加上mutable
mutable:mutable 关键字,如果有,则表示在函数体中可以修改捕获变量,根据具体需求决定是否需要省略。
异常列表:noexcept / throw(…),和普通函数的异常列表一样,可省略,即代表可能抛出任何类型的异常。
返回类型:和函数的返回类型一样。可省略,如省略,编译器将自动推导返回类型。
函数体:代码实现。可省略,但是没意义。
lambda表达式允许捕获一定范围内的变量:
[] 不捕获任何变量
[&] 引用捕获,捕获外部作用域所有变量,在函数体内当作引用使用
[=] 值捕获,捕获外部作用域所有变量,在函数内内有个副本使用
[=, &a] 值捕获外部作用域所有变量,按引用捕获a变量
[a] 只值捕获a变量,不捕获其它变量
[this] 捕获当前类中的this指针
#include <iostream>
using namespace std;
void func(int x, int y)
{
int a = 1;
int b = 2;
cout << "a " << a << " b " << b << endl;//b==2
[=, &x](int z)mutable {
int c = a;
int d = x;
b++;
x++;
cout << "b " << b << " x " << x << endl;//b==3
cout << "z " << z << endl;
}(100);
cout << "a " << a << " b " << b << endl;//b==2
}
int main(int argc, char* argv[])
{
func(111, 222);
return 0;
}
C++ 11
#include<iostream>
#include<functional>
using namespace std;
//auto func = [capture] (params) opt -> ret { func_body; };
//
[捕获列表] (形参列表) mutable 异常列表-> 返回类型{ 函数体 }
//
捕获列表:定义lambda函数所在的作用域中的指定变量可以在lambda函数中使用
/*
其中func是可以当作lambda表达式的名字,作为一个函数使用,capture是捕获列表,params是参数表,opt是函数选项(mutable之类), ret是返回值类型,func_body是函数体。
各项的含义:
捕获列表:捕获外部变量,捕获的变量可以在函数体中使用,可以省略,即不捕获外部变量。
形参列表:和普通函数的形参列表一样。可省略,即无参数列表
mutable:mutable 关键字,如果有,则表示在函数体中可以修改捕获变量,根据具体需求决定是否需要省略。
异常列表:noexcept / throw(...),和普通函数的异常列表一样,可省略,即代表可能抛出任何类型的异常。
返回类型:和函数的返回类型一样。可省略,如省略,编译器将自动推导返回类型。
函数体:代码实现。可省略,但是没意义。
lambda表达式允许捕获一定范围内的变量:
[]
不捕获任何变量
[&]
引用捕获,捕获外部作用域所有变量,在函数体内当作引用使用
[=]
值捕获,捕获外部作用域所有变量,在函数内内有个副本使用
[=, &a]
值捕获外部作用域所有变量,按引用捕获a变量
[a]
只值捕获a变量,不捕获其它变量
[this]
捕获当前类中的this指针
*/
int main()
{
//lambda in C++ 11
//g++ lambda.cpp -Wall -std=c++11 -o test && ./test
//step 1 打印输出
auto basiclambda01=[](){cout<<"hello lambda"<<endl;} ;//注:参数列表为空时可以省略“ ( )”
,如果函数内容是单个返回语句(只有一条返回语句),那么“ -> 返回类型”也可以省略
basiclambda01();
//cout<<[](){cout<<"hello lambda"<<endl;}<<"n";
//step 2 返回值
auto basiclambda02=[](int a, int b)->int{
cout<<"param and ret lambda"<<endl;
return a+b;
};
int result = basiclambda02(3,4);
cout<<result<<endl;
cout<<basiclambda02(4,5)<<endl;
//step 3
使用捕获列表, 引用捕获, 可以修改data
int data = 2;
auto basicLambda03 =
[&data](){
cout<<"inside basicLambda03 = " <<++data<<endl;
};
basicLambda03();
cout<<"outside basicLambda03 = " << data<<endl;// same to inside basicLambda03
//step 4
值捕获
/*
使用传引用的方式能够在lambda函数体内部修改传入的捕捉变量。但是如果是以传值的方式进行捕捉,即便捕捉变量为非const,在lambda函数内部也不能够修改变量的值,
因为传入的是捕捉变量的副本,如果想要修改,那么需要在函数定义时加上关键字mutable,此时即便传入参数为空," () "也不可省略
*/
auto basicLambda04 =
[data]()mutable {
cout<<"inside basicLambda04 = " <<++data<<endl;
};
basicLambda04();
cout<<"outside basicLambda04 = " << data<<endl;// not same to inside basicLambda03
//step 5
隐式捕获
// 手动书写捕获列表有时候是非常复杂的,这种机械性的工作可以交给编译器来处理,这时候可以在捕获列表中写一个
// & 或 = 向编译器声明采用引用捕获或者值捕获.
//隐式引用捕获
auto basicLambda05 =
[&](){
cout<<"inside basicLambda05 = " <<++data<<endl;
};
basicLambda05();
cout<<"outside basicLambda05 = " << data<<endl;// same to inside basicLambda05
//隐式值捕获
auto basicLambda06 =
[=]()mutable{
cout<<"inside basicLambda06 = " <<++data<<endl;
};
basicLambda06();
cout<<"outside basicLambda06 = " << data<<endl;// same to inside basicLambda06
// 总结一下,捕获提供了lambda 表达式对外部值进行使用的功能,捕获列表的最常用的四种形式可以是:
// [] 空捕获列表
// [name1, name2, ...] 捕获一系列变量
// [&] 引用捕获, 让编译器自行推导捕获列表
// [=] 值捕获, 让编译器执行推导引用列表
return 0;
}
C++ 14
#include <iostream>
#include <memory>//for make_unique
#include <utility>
int main() {
//lambda in C++ 14
//g++ lambda.cpp -Wall -std=c++11 -o test && ./test
//step 1 表达式捕获
/*
之前提到的值捕获、引用捕获都是已经在外层作用域声明的变量,因此这些捕获方式捕获的均为左值,而不能捕获右值。
C++14 给与了我们方便,允许捕获的成员用任意的表达式进行初始化,这就允许了右值的捕获,
被声明的捕获变量类型会根据表达式进行判断,判断方式与使用 auto本质上是相同的:
*/
auto important = std::make_unique<int>(1);
auto add01 = [v1 = 1, v2 = std::move(important)](int x, int y) -> int {
return x+y+v1+(*v2);
};
std::cout << add01(3,4) << std::endl;
/*
在上面的代码中,important是一个独占指针,是不能够被捕获到的,这时候我们需要将其转移为右值,在表达式中初始化。
*/
//step 3 泛型 Lambda
auto add02 = [](auto x, auto y) {
return x+y;
};
std::cout <<add02(1, 2)<< std::endl;
std::cout <<add02(1.1, 2.2)<< std::endl;
return 0;
}
c++11 中 auto
关键字不能够用在参数表里,这是因为这样的写法会与模板的功能产生冲突。
但是 Lambda 表达式并不是普通函数,所以 Lambda 表达式并不能够模板化。
这就为我们造成了一定程度上的麻烦:参数表不能够泛化,必须明确参数表类型。
幸运的是,这种麻烦只存在于 C++11 中,从 C++14 开始,
Lambda 函数的形式参数可以使用 auto
关键字来产生意义上的泛型
lambda函数本质
使用 lambda 表达式捕获列表捕获外部变量,如果希望去修改按值捕获的外部变量,那么应该如何处理呢?这就需要使用 mutable 选项,被mutable修改是lambda表达式就算没有参数也要写明参数列表,并且可以去掉按值捕获的外部变量的只读(const)属性。
int a = 0;
auto f1 = [=] {return a++; };
// error, 按值捕获外部变量, a是只读的
auto f2 = [=]()mutable {return a++; };
// ok
最后再剖析一下为什么通过值拷贝的方式捕获的外部变量是只读的:
lambda表达式的类型在C++11中会被看做是一个带operator()的类,即仿函数。
按照C++标准,lambda表达式的operator()默认是const的,一个const成员函数是无法修改成员变量值的。
mutable 选项的作用就在于取消 operator () 的 const 属性。
因为 lambda 表达式在 C++ 中会被看做是一个仿函数,因此可以使用std::function和std::bind来存储和操作lambda表达式:
#include <iostream>
#include <functional>
using namespace std;
int main(void)
{
// 包装可调用函数
std::function<int(int)> f1 = [](int a) {return a; };
// 绑定可调用函数
std::function<int(int)> f2 = bind([](int a) {return a; }, placeholders::_1);
// 函数调用
cout << f1(100) << endl;
cout << f2(200) << endl;
return 0;
}
对于没有捕获任何变量的 lambda 表达式,还可以转换成一个普通的函数指针:
using func_ptr = int(*)(int);
// 没有捕获任何外部变量的匿名函数
func_ptr f = [](int a)
{
return a;
};
// 函数调用
f(1314);
最后
以上就是能干路灯为你收集整理的C++ 11 & 14 中的Lambda表达式的全部内容,希望文章能够帮你解决C++ 11 & 14 中的Lambda表达式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复