C++11Lambda表达式-创新互联

1. Lamda 表达式介绍

lambda表达式:也就是匿名函数,也可以称作闭包(Closure),字面意义是没有名字的函数。它可以很方便地让我们随手定义函数,并把函数当做参数,给其他函数调用。这样的代码写起来很简洁,读起来也直观。

创新互联建站专注于铜鼓企业网站建设,响应式网站开发,商城网站定制开发。铜鼓网站建设公司,为铜鼓等地区提供建站服务。全流程定制制作,专业设计,全程项目跟踪,创新互联建站专业和态度为您提供的服务

举例1:对序列{0,11,3,19,22,7,1,5}如何从小到大排序呢?,可以通过定义匿名函数的方式实现。

#include 
#include#includeusing namespace std;

int main()
{vectorvec {0,11,3,19,22,7,1,5};
	auto f =[](int a, int b)
	{return  ab从大到小排序
	};
	sort(vec.begin(),vec.end(),f);
}
  • sort第3个参数,传入的就是匿名函数,这样的写法相比原始的c++要灵活简洁很多。
2 Lambda表达式语法

在这里插入图片描述

  • 首先需要写一个方括号[ ], 里面可以传入捕获变量的形式或者为空
  • 然后是()包起来的参数列表
  • 然后是->并跟着返回值类型
  • 最后是函数的实现主体body

举例2:

auto f = [](int a, int b) ->int {return a+b;
};
cout<< f(1,2)<
>>3
  • 方括号[ ]里面是空的,表示不捕获外围的变量
  • 这里lambda的返回值int 是可以忽略的,因为编译器可通过return a+b以及参数类型,自行推断它的返回类型。所以可以等价于如下:
auto f = [](int a, int b){return a+b;
};
cout<< f(1,2)<

一般情况下,不指定 lambda 表达式的返回值,编译器会根据 return 语句自动推导返回值的类型,但需要注意的是 labmda表达式不能通过列表初始化自动推导出返回值类型。

// ok,可以自动推导出返回值类型
auto f = [](int i)
{return i;
}

// error,不能推导出返回值类型
auto f1 = []()
{return {1, 2};	// 基于列表初始化推导返回值,错误
}

2.1 变量捕获(Capture Cluse)
  • 变量捕获就是方括号部分,作用是让我们匿名函数可以访问,甚至修改函数外部的变量,
  • [ ]中可以指定一些外围的变量,这样在匿名函数内部就可以访问这些变量。如果是空的[]表示不捕获任何变量。
  • 如果变量名前面有引用符号&,表示按引用捕获,可以修改外部变量的值。如果不加引用符号&,就表示按值捕获,不能修改外围变量的值

举例3:

int N =100, M =10;

auto g = [N,&M](int i)
{M=20;
	return N*i;
}

cout<< g(10)<< endl;
cout<< M<< endl;

输出

>>1000   // 100*10
>>20     // 匿名函数内部M赋值为20

注:这里N是没有加&号,只能访问,不能修改。如果尝试给N赋值编译器会报错

[&]

按引用捕获:如果捕获的语句只有引用符号:[&],表示所有的变量都按引用捕获,即可以修改外围变量的值。

[=]

按值捕获: 如果捕获语句为[=], 表示所有变量都按值捕获。

[&,=N]

可以单独指定一些变量按值捕获,其他变量都按引用捕获。[&,=N],这种写法表示按值捕获变量N,其他变量按引用捕获。

[this]

可以用this捕获当前实例的指针,在c++17之后,还可以用[*this]按值捕获该实例。

举例

#include#includeusing namespace std;

class Test
{public:
    void output(int x, int y)
    {auto x1 = [] {return m_number; };                      // error
        auto x2 = [=] {return m_number + x + y; };             // ok
        auto x3 = [&] {return m_number + x + y; };             // ok
        auto x4 = [this] {return m_number; };                  // ok
        auto x5 = [this] {return m_number + x + y; };          // error
        auto x6 = [this, x, y] {return m_number + x + y; };    // ok
        auto x7 = [this] {return m_number++; };                // ok
    }
    int m_number = 100;
};
  • x1:错误,没有捕获外部变量,不能使用类成员 m_number
  • x2:正确,以值拷贝的方式捕获所有外部变量
  • x3:正确,以引用的方式捕获所有外部变量
  • x4:正确,捕获 this 指针,可访问对象内部成员
  • x5:错误,捕获 this 指针,可访问类内部成员,没有捕获到变量 x,y,因此不能访问。
  • x6:正确,捕获 this 指针,x,y
  • x7:正确,捕获 this 指针,并且可以修改对象内部变量的值
int main(void)
{int a = 10, b = 20;
    auto f1 = [] {return a; };                        // error
    auto f2 = [&] {return a++; };                     // ok
    auto f3 = [=] {return a; };                       // ok
    auto f4 = [=] {return a++; };                     // error
    auto f5 = [a] {return a + b; };                   // error
    auto f6 = [a, &b] {return a + (b++); };           // ok
    auto f7 = [=, &b] {return a + (b++); };           // ok

    return 0;
}
  • f1:错误,没有捕获外部变量,因此无法访问变量 a
  • f2:正确,使用引用的方式捕获外部变量,可读写
  • f3:正确,使用值拷贝的方式捕获外部变量,可读
  • f4:错误,使用值拷贝的方式捕获外部变量,可读不能写
  • f5:错误,使用拷贝的方式捕获了外部变量 a,没有捕获外部变量 b,因此无法访问变量 b
  • f6:正确,使用拷贝的方式捕获了外部变量 a,只读,使用引用的方式捕获外部变量 b,可读写
  • f7:正确,使用值拷贝的方式捕获所有外部变量以及 b 的引用,b 可读写,其他只读

在匿名函数内部,需要通过 lambda 表达式的捕获列表控制如何捕获外部变量,以及访问哪些变量。默认状态下 lambda 表达式无法修改通过复制方式捕获外部变量,如果希望修改这些外部变量,需要通过引用的方式进行捕获。

2.2 使用mutable修改按值捕获的外部变量

使用 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#includeusing namespace std;

int main(void)
{// 包装可调用函数
    std::functionf1 = [](int a) {return a; };
    // 绑定可调用函数
    std::functionf2 = 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);
2.2 其他特性(c++14后)

支持在捕获语句中定义新变量

在c++14之后,可以在捕获语句中定义新的变量,并且初始化,这些变量无需出现在匿名函数外围环境中

void foo()
{int N=100,M=10;
	auto g = [N,&M,K=5](int i)
	{M = 20;
		cout<< K<< endl;
		return N*i;
	};
	
	cout<

在匿名函数中,引入了一个新的变量K,并且给它赋值5,然后再函数主体中打印它

参数类别支持auto类型
[](auto a, auto b) {return a+b;}
在c++14新增的功能,参数列表支持auto类型,这让匿名函数变得更通用,这样只要支出加号类型的变量都可以利用该匿名函数。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


网页名称:C++11Lambda表达式-创新互联
文章来源:http://cdiso.cn/article/jeeds.html