Lambda表达式

lambda表达式是C++11最重要也是最常用的特性之一,这是现代编程语言的一个特点,lambda表达式由如下一些特点:

1. 声明式的编程风格:就地匿名定义目标函数或者函数对象,不需要额外写一个命名函数或者函数对象

2. 简洁:避免了代码膨胀和功能分散,让开发更加高效

3. 在需要的时间和地点实现功能闭包,使程序更加灵活

Lambda表达式语法:[capture ] ( params ) mutable exception attribute -> return-type { body }

其中cature是捕获列表,params是参数列表,opt(mutable,exception)是函数选项,ret是返回值类型,body是函数体。

其中capture为定义外部变量是否可见(捕获),若为空,则表示不捕获所有外部变量,即所有外部变量均不可访问,

= 表示所有外部变量均以值的形式捕获,在body中访问外部变量时,访问的是外部变量的一个副本,类似函数的值传递,因此在body中对外部变量的修改均不影响外部变量原来的值。

& 表示以引用的形式捕获,后面加上需要捕获的变量名,没有变量名,则表示以引用形式捕获所有变量,类似函数的引用传递,body操作的是外部变量的引用,因此body中修改外部变量的值会影响原来的值。

params就是函数的形参,和普通函数类似,不过若没有形参,这个部分可以省略。

mutalbe 可以修改按值传递进来的拷贝(注意是修改拷贝,而不是值本身)

exception声明可能抛出的异常 throw

return-type表示返回类型,如果能够根据返回语句自动推导,则可以省略,

body即函数体。

除了capture和body是必需的,其他均可以省略。

Lambda表达式是用于创建匿名函数的。Lambda 表达式使用一对方括号作为开始的标识,类似于声明一个函数,只不过这个函数没有名字,也就是一个匿名函数。Lambda 表达式的返回值类型是语言自动推断的。如果不想让Lambda表达式自动推断类型,或者是Lambda表达式的内容很复杂,不能自动推断,则这时必须显示指定Lambda表达式返回值类型,通过”->”。

引入Lambda表达式的前导符是一对方括号,称为Lambda引入符(lambda-introducer):

(1)、[]        // 不捕获任何外部变量

(2)、[=]       //以值的形式捕获所有外部变量

(3)、[&]       //以引用形式捕获所有外部变量

(4)、[x, &y]    // x 以传值形式捕获,y 以引用形式捕获

(5)、[=, &z]    // z 以引用形式捕获,其余变量以传值形式捕获

(6)、[&,x]     // x 以值的形式捕获,其余变量以引用形式捕获

(7)、[this]    捕获当前类中的this指针

(7)、对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针。但是,对于[]的形式,如果要使用 this 指针,必须显式传入,如:[this]() { this->someFunc(); }();

#include "Lambda.hpp"
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <string>
 
///
// reference: http://en.cppreference.com/w/cpp/language/lambda
int test_lambda1()
{
	/*
	[]		Capture nothing (or, a scorched earth strategy?)
	[&]		Capture any referenced variable by reference
	[=]		Capture any referenced variable by making a copy
	[=, &foo]	Capture any referenced variable by making a copy, but capture variable foo by reference
	[bar]		Capture bar by making a copy; don't copy anything else
	[this]		Capture the this pointer of the enclosing class
	*/
	int a = 1, b = 1, c = 1;
 
	auto m1 = [a, &b, &c]() mutable {
		auto m2 = [a, b, &c]() mutable {
			std::cout << a << b << c << '\n';
			a = 4; b = 4; c = 4;
		};
		a = 3; b = 3; c = 3;
		m2();
	};
 
	a = 2; b = 2; c = 2;
 
	m1();                             // calls m2() and prints 123
	std::cout << a << b << c << '\n'; // prints 234
 
	return 0;
}
 
///
// reference: http://en.cppreference.com/w/cpp/language/lambda
int test_lambda2()
{
	std::vector<int> c = { 1, 2, 3, 4, 5, 6, 7 };
	int x = 5;
	c.erase(std::remove_if(c.begin(), c.end(), [x](int n) { return n < x; }), c.end());
 
	std::cout << "c: ";
	std::for_each(c.begin(), c.end(), [](int i){ std::cout << i << ' '; });
	std::cout << '\n';
 
	// the type of a closure cannot be named, but can be inferred with auto
	auto func1 = [](int i) { return i + 4; };
	std::cout << "func1: " << func1(6) << '\n';
 
	// like all callable objects, closures can be captured in std::function
	// (this may incur unnecessary overhead)
	std::function<int(int)> func2 = [](int i) { return i + 4; };
	std::cout << "func2: " << func2(6) << '\n';
 
	return 0;
}
 
///
// reference: https://msdn.microsoft.com/zh-cn/library/dd293608.aspx
int test_lambda3()
{
	// The following example contains a lambda expression that explicitly captures the variable n by value
	// and implicitly captures the variable m by reference:
	int m = 0;
	int n = 0;
	[&, n](int a) mutable { m = ++n + a; }(4);
 
	// Because the variable n is captured by value, its value remains 0 after the call to the lambda expression.
	// The mutable specification allows n to be modified within the lambda.
	std::cout << m << std::endl << n << std::endl;
 
	return 0;
}
 
//
// reference: https://msdn.microsoft.com/zh-cn/library/dd293608.aspx
template <typename C>
void print(const std::string& s, const C& c)
{
	std::cout << s;
 
	for (const auto& e : c) {
		std::cout << e << " ";
	}
 
	std::cout << std::endl;
}
 
void fillVector(std::vector<int>& v)
{
	// A local static variable.
	static int nextValue = 1;
 
	// The lambda expression that appears in the following call to
	// the generate function modifies and uses the local static 
	// variable nextValue.
	generate(v.begin(), v.end(), [] { return nextValue++; });
	//WARNING: this is not thread-safe and is shown for illustration only
}
 
int test_lambda4()
{
	// The number of elements in the vector.
	const int elementCount = 9;
 
	// Create a vector object with each element set to 1.
	std::vector<int> v(elementCount, 1);
 
	// These variables hold the previous two elements of the vector.
	int x = 1;
	int y = 1;
 
	// Sets each element in the vector to the sum of the 
	// previous two elements.
	generate_n(v.begin() + 2,
		elementCount - 2,
		[=]() mutable throw() -> int { // lambda is the 3rd parameter
		// Generate current value.
		int n = x + y;
		// Update previous two values.
		x = y;
		y = n;
		return n;
	});
	print("vector v after call to generate_n() with lambda: ", v);
 
	// Print the local variables x and y.
	// The values of x and y hold their initial values because 
	// they are captured by value.
	std::cout << "x: " << x << " y: " << y << std::endl;
 
	// Fill the vector with a sequence of numbers
	fillVector(v);
	print("vector v after 1st call to fillVector(): ", v);
	// Fill the vector with the next sequence of numbers
	fillVector(v);
	print("vector v after 2nd call to fillVector(): ", v);
 
	return 0;
}
 
/
// reference: http://blogorama.nerdworks.in/somenotesonc11lambdafunctions/
 
template<typename T>
std::function<T()> makeAccumulator(T& val, T by) {
	return [=, &val]() {
		return (val += by);
	};
}
 
int test_lambda5()
{
	int val = 10;
	auto add5 = makeAccumulator(val, 5);
	std::cout << add5() << std::endl;
	std::cout << add5() << std::endl;
	std::cout << add5() << std::endl;
	std::cout << std::endl;
 
	val = 100;
	auto add10 = makeAccumulator(val, 10);
	std::cout << add10() << std::endl;
	std::cout << add10() << std::endl;
	std::cout << add10() << std::endl;
 
	return 0;
}
 
 
// reference: http://blogorama.nerdworks.in/somenotesonc11lambdafunctions/
class Foo_lambda {
public:
	Foo_lambda() {
		std::cout << "Foo_lambda::Foo_lambda()" << std::endl;
	}
 
	Foo_lambda(const Foo_lambda& f) {
		std::cout << "Foo_lambda::Foo_lambda(const Foo_lambda&)" << std::endl;
	}
 
	~Foo_lambda() {
		std::cout << "Foo_lambda~Foo_lambda()" << std::endl;
	}
};
 
int test_lambda6()
{
	Foo_lambda f;
	auto fn = [f]() { std::cout << "lambda" << std::endl; };
	std::cout << "Quitting." << std::endl;
	return 0;
}

Lambda函数本质

使用 lambda 表达式捕获列表捕获外部变量,如果希望去修改按值捕获的外部变量,那么应该如何处理呢?这就需要使用 mutable 选项,被mutable修改是lambda表达式就算没有参数也要写明参数列表,并且可以去掉按值捕获的外部变量的只读(const)属性。

int a = 0;
auto f1 = [=] {return a++; };              // error, 按值捕获外部变量, a是只读的
auto f2 = [=]()mutable {return a++; };     // ok

最后再剖析一下为什么通过值拷贝的方式捕获的外部变量是只读的:

1. lambda表达式的类型在C++11中会被看做是一个带operator()的类,即仿函数。


2. 按照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);

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值