lambda 表达式( https://leanpub.com/cpplambda ):
– 为了更灵活地实现可调用对象而引入
– C++11 ~ C++20 持续更新
lambda 表达式会被编译器翻译成类进行处理
[捕获] (参数) 说明符 -> 返回类型 {函数体}
[]{};
在第一个例子中,可以看到一个简单的Lambda表达式。它只需要函数体的[ ],然后函数体为空{ }。参数列表- ( ) -是可选的,在这种情况下不需要。
[](float f, int a) { return a * f; };
[](int a, int b) { return a < b; };
在第二个示例中,可能是最常见的一个,您可以看到参数被传递到( )中,就像对常规函数一样。不需要返回类型,因为编译器将自动推导它。
[](MyClass t) -> int { auto a = t.compute(); print(a); return a; };
在上面的例子中,我们显式地设置了返回类型。自C++11以来,箭头+类型表示返回类型也可用于常规函数声明。
捕获:针对函数体中使用的局部自动对象进行捕获
另外,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只能在编译期调用。
[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)。
示例:
#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
#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
#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
如果你复制一个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
#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值捕获,其他引用捕获
}
#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();
}
#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
#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
捕获时计算( C++14 )
即调用函数表达式( Immediately-Invoked Function Expression, IIFE )
即调用函数表达式:先创建Lambda表达式,并不分配给任何闭包对象,然后它被( )调用。
使用 auto 避免复制( C++14 )
Lifting ( C++14 )
递归调用( C++14 )
#include <iostream>
int main() {
int x = 8, y = 10;
auto l1 = [z = x + y]()
{
return z;
};
std::cout << l1() << std::endl;
}
结果:
18
#include <iostream>
int main() {
int x = 8, y = 10;
auto l1 = [z = x + y]()
{
return z;
}();
std::cout << l1 << std::endl;
}
结果:
18
#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>
#include <iostream>
int main() {
int x = 8;
auto l1 = [](auto a)
{
return ++a;
};
std::cout << l1(x) << std::endl;
}
结果:
9
#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
#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
#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》