当前位置: 首页 > 工具软件 > Lam > 使用案例 >

C++中 lambda 表达式

董小林
2023-12-01

lambda 表达式

lambda 表达式( https://leanpub.com/cpplambda ):
– 为了更灵活地实现可调用对象而引入
– C++11 ~ C++20 持续更新

  • C++11 引入 lambda 表达式
  • C++14 支持初始化捕获、泛型 lambda
  • C++17 引入 constexpr lambda , *this 捕获
  • C++20 引入 concepts ,模板 lambda

lambda 表达式会被编译器翻译成类进行处理

lambda 表达式的基本组成部分
[捕获] (参数) 说明符 -> 返回类型 {函数体}
– 参数与函数体
  1. 最简单的lambda 表达式:
[]{};

在第一个例子中,可以看到一个简单的Lambda表达式。它只需要函数体的[ ],然后函数体为空{ }。参数列表- ( ) -是可选的,在这种情况下不需要。

  1. 带两个参数的lambda 表达式:
[](float f, int a) { return a * f; };
[](int a, int b) { return a < b; };

在第二个示例中,可能是最常见的一个,您可以看到参数被传递到( )中,就像对常规函数一样。不需要返回类型,因为编译器将自动推导它。

– 返回类型
  1. 箭头+类型表示返回类型:
[](MyClass t) -> int { auto a = t.compute(); print(a); return a; };

在上面的例子中,我们显式地设置了返回类型。自C++11以来,箭头+类型表示返回类型也可用于常规函数声明。

– 捕获

捕获:针对函数体中使用的局部自动对象进行捕获

  • 值捕获、引用捕获与混合捕获
  • this 捕获
  • 初始化捕获( C++14 )
  • *this 捕获( C++17 )

另外,capture 指定了在可见域范围内 lambda 表达式的代码内可见得外部变量的列表,具体解释如下:

捕获[capture]具体解释
[a,&b]a变量以值的方式被捕获,b以引用的方式被捕获。
[this]以值的方式捕获 this 指针。
[&]以引用的方式捕获所有的外部自动变量。
[=]以值的方式捕获所有的外部自动变量。
[]不捕获外部的任何变量。
– 说明符
  • mutable修饰符说明 lambda 表达式体内的代码可以修改被捕获的变量,并且可以访问被捕获对象的 non-const 方法。

  • exception 说明 lambda 表达式是否抛出异常(noexcept),以及抛出何种异常,类似于void f() throw(X, Y)。

  • attribute 用来声明属性。

  • constexpr (C++17) / consteval (C++20)……

    注意:constexpr与consteval区别:constexpr不仅能在运行期调用,也可以在编译期调用;consteval只能在编译期调用。

  1. mutable exception:
[x](int a, int b) mutable { ++ x; return a < b; };
[](float param) noexcept { return param* param; };
[x](int a, int b) mutable noexcept { ++ x; return a < b; };

这个例子说明,在lambda 表达式中,可以使用其他的说明符。在上述代码中,我们使用了mutable( 以便更改捕获变量 ),也没有例外。第三个lambda 表达式使用mutable和noexcept,它们必须按那个顺序出现( 当编译器不通过时 ,您不能写noexcept mutable)。

– 模板形参( C++20 )

示例:

#include <iostream>

int main() {
	int x = 8;
	auto l1 = []<typename T>(T val)
	{
		return val + 10;
	};
	constexpr int y = l1(10);
	std::cout << y << std::endl;
}

结果:

20
lambda 表达式示例
例1:打印输出
#include <algorithm>
#include <iostream>
#include <vector>

void PrintFunc(int x) {
	std::cout << x << " ";
}

int main() {
	std::vector<int> x = { 1,2,3,4 };
	std::for_each(x.begin(), x.end(), PrintFunc);
	std::cout << "\nlambda:" << std::endl;
	std::for_each(x.begin(), x.end(), [](int x) {std::cout << x << " "; });

}

结果:

1 2 3 4
lambda:
1 2 3 4
例2:std::function与Lambda比较
#include <iostream>
#include <functional>

int main() {
	const auto myLambda = [](int a) noexcept->double {
		return 2.0 * a;
	};
	const std::function<double(int)> myFunc =
		[](int a)noexcept->double {return 2.0 * a; };

	std::cout << "Sizeof(myLambda) is :" << sizeof(myLambda) << std::endl;
	std::cout << "Sizeof(myFunc) is :" << sizeof(myFunc) << std::endl;
	return myFunc(10) == myLambda(10);
}

结果:

Sizeof(myLambda) is :1
Sizeof(myFunc) is :64
例3:复制一个Lambda

如果你复制一个Lambda,那么你也复制它的状态。这一点在我们谈到捕获变量时很重要。在这种情况下,类将捕获的变量存储为成员字段,执行lambda拷贝将复制这些数据成员字段。

#include <iostream>
#include <type_traits>

int main() {
	const auto my1Lambda = [](int a) noexcept{return 2.0 * a;};
	const auto my2Lambda = my1Lambda;

	std::cout << std::is_same<decltype(my1Lambda), decltype(my2Lambda)>::value << std::endl;
}

结果:

1
例4:捕获
#include <iostream>

int main() {
	int x = 2, y = 3;
	const auto l1 = []() {return 1;};// 无捕捉
	const auto l2 = [=]() {return x; };// 所有值捕获
	const auto l3 = [&]() {return y; };// 所有引用捕获
	const auto l4 = [x]() {return x; };// x值捕获
	const auto l5 = [&y]() {return y; };// y引用捕获
	const auto l6 = [x,&y]() {return x * y; };// x值捕获,y引用捕获
	const auto l7 = [=,&x]() {return x + y; };// x引用捕获,其他值捕获
	const auto l8 = [&, y]() {return x - y; };// y值捕获,其他引用捕获

}
例5:this使用
#include <iostream>
#include <functional>

struct Str
{
	auto fun()
	{
		int val = 1;
		auto mylambda = [val, this]()
		{
			return val > x;
		};
		return mylambda();
	}
	int x = 10;
};

int main() {
	Str s;
	s.fun();
}
例6:捕获初始化
#include <iostream>
#include <functional>

int main() {
	int x = 8;
	auto l1 = [y = x](int val)
	{
		return val > y;
	};
	std::cout << l1(10) << std::endl;
}

结果:

1
例7:使用constexpr,可在编译期执行。
#include <iostream>

int main() {
	int x = 8;
	auto l1 = [](int val) constexpr
	{
		return val + 10;
	};
	constexpr int y = l1(10);
	std::cout << y << std::endl;
}

结果:

20
lambda 表达式的深入应用
  • 捕获时计算( C++14 )

  • 即调用函数表达式( Immediately-Invoked Function Expression, IIFE )

    即调用函数表达式:先创建Lambda表达式,并不分配给任何闭包对象,然后它被( )调用。

  • 使用 auto 避免复制( C++14 )

  • Lifting ( C++14 )

  • 递归调用( C++14 )

例8:捕获时计算
#include <iostream>

int main() {
	int x = 8, y = 10;

	auto l1 = [z = x + y]()
	{
		return z;
	};
	
	std::cout << l1() << std::endl;
}

结果:

18
例9:即调用函数表达式
#include <iostream>

int main() {
	int x = 8, y = 10;

	auto l1 = [z = x + y]()
	{
		return z;
	}();
	
	std::cout << l1 << std::endl;
}

结果:

18
例10:生成html
#include <iostream>
#include <string>

void ValidateHTML(const std::string&) { }
std::string BuildAHref(const std::string& link, const std::string& text) {
	const std::string html = [&link, &text] {
		const auto& inText = text.empty() ? link : text;
		return "<a href= \" " + link + " \" >" + inText + "</a>";
	}(); // call!
	ValidateHTML(html);
	return html;
}

int main() {
	try {
		const auto ahref = BuildAHref("www.leanpub.com", "Leanpub Store");
		std::cout << ahref;
	}
	catch (...) {
		std::cout << "bad format...";
	}
}

结果:

<a href= " www.leanpub.com " >Leanpub Store</a>
例11:使用 auto 避免复制
#include <iostream>

int main() {
	int x = 8;
	auto l1 = [](auto a)
	{
		return ++a;
	};
	
	std::cout << l1(x) << std::endl;
}

结果:

9
例12:实现map中键值求和
#include <iostream>
#include <functional>
#include <algorithm>
#include <map>

void PrintFunc(int x)
{
	std::cout << x << " ";
}

int main() {
	std::map<int, int> m{ {1,2},{3,4},{4,5},{5,6} };
	auto lam = [](const auto& p)// auto表示 std::pair<const int, int>
	{
		return p.first + p.second;
	};

	std::vector<int> v;
	for (auto &i : m)
	{
		v.push_back(lam(i));
	}
	std::for_each(v.cbegin(), v.cend(), PrintFunc);
}

结果:

3 7 9 11
例13:lambda实现函数重载(Lifting)
#include <iostream>

auto fun(int val)
{
	return val + 10;
}
auto fun(double val)
{
	return val + 12.0;
}
int main() {
	auto lam = [](auto x)
	{
		return fun(x);
	};	
	std::cout << lam(1) << std::endl;	
}

结果:

11
例14:求n的阶层(递归调用)
#include <iostream>

int factorial(int n)
{
	return n > 1 ? n * factorial(n - 1) : 1;
}

int main() {
	
	auto factorial1 = [](int n)
	{
		auto f_impl = [](int n, const auto& impl)->int
		{
			return n > 1 ? n * impl(n - 1, impl) : 1;
		};
		return f_impl(n, f_impl);
	};
	
	std::cout << factorial(10) << std::endl;
	std::cout << factorial1(10) << std::endl;
	
}

结果:

3628800
3628800

参考资料:

《C++ Primer 中文版》(第 5 版)

《大道至简—c++stl(标准模板库)精解》

《c++语言程序设计》(第2版)

《C++ Lambda Story》

(7条消息) C++中Lamda表达式_windmeeeeee的博客-CSDN博客

 类似资料: