什么是λ演算

首先,这个λ演算是非常牛逼的一个东西,可以看一下维基百科——λ的介绍

C++ 适用 λ 表达式

众所周知,如果一个工程中用了很多函数名,最终会导致命名污染。

不妨假设如下场景,如果你有一个函数,需要重复写一段代码,你会选择将这段代码封装成另一个函数FUN。但是,一个函数就需要一个名字,你封装成的另一个函数FUN就需要一个名字,但是这个FUN函数,你在其他地方又用不到了,那么就可以尝试适用匿名函数来实现了。

简单用法

int function() {
	auto add = [](int a, int b){
        if(a % 2) return a+b;
        return a;
    };
	cout << add(3,4);
	return 0;
}

// 上面这个结果就会输出 7
// 用到这里你会发现lambda没有什么牛逼之处
// 但是,如果你不想这个add函数在外部用到,而仅仅是function函数中使用
// 那么它将带来很大的遍历,你不需要考虑如何去命名

细节用法

​ 可能不太理解为什么像上述写法这么写 [](){},这个为什么构成lambda表达式

  • []部分:这里是表示捕获部分,为什么需要捕获呢?

    因为add函数也许需要用到function函数中的东西。

    有人发出疑问?不是可以传参吗?

    但是,传参造成了很多不必要的麻烦,如:你需要先声明传参类型,然后你一般需要传引用,最后导致参数列表变得冗长

    []很好的解决这个问题

    • []:为空,那么就是不捕获任何参数,就是lambda表达式中不能使用外部的变量
    • [=]:值捕获,并且捕获所有变量
    • [&]:引用捕获,也是捕获所有变量
    • [this]:捕获当前的类的指针
    • [a]:只捕获a的值
    • [&a]:捕获a的引用
    • [=, &a, &b]:除了a,b引用传递外,其余的值传递
    • [&, a, b]:除了a,b值传递外,其余的引用传递
  • ()部分:这里是参数列表,和普通函数一样,值传递和引用传递都可以

  • {}部分:代码块


​ 除此之外,还和普通函数一样,有说明符和返回值的约束。

// 说明符的介绍
auto f = [&]() mutable
{
    return 1;
}
  • mutable:允许 函数体 修改各个复制捕获的对象,以及调用其非 const 成员函数;
  • constexpr:显式指定函数调用运算符为 constexpr函数。此说明符不存在时,若函数调用运算符恰好满足针对 constexpr函数的所有要求,则它也会是 constexpr; (C++17 起)
  • consteval:指定函数调用运算符为立即函数。不能同时使用 constevalconstexpr。(C++20 起)
// 返回值介绍
// 限制返回 int
auto f = [&]() -> int
{
    return 1;
}

补充说明

​ 有人发现了,前面都是使用auto关键词声明的,那么可不可以声明为具体类型了,答案是也可以的。既然是匿名函数,肯定没有固定的typeid,经过测试,那怕完全相同的两个函数,其typeid都是不一样的。就像int a = 1; int b = 1;a,b不是用一个东西,仅仅是两个变量的值一样,但是a,btypeid是一样的。auto f1 = [](){}; auto f2 = [](){};中,f1, f2他们仅仅是地址对应的值相同而已,都是只执行了[](){}这个函数。

​ 但是,前面说可以声明为具体类型,那么,该怎么实现呢?答案是:函数指针

  • 注意,这里的返回值-> int不能省略
  • 而且,如果下述实现,是有typeid(f)==typeid(g)
  • 并且还有typeid(f) == typeid(g) == typeid(h)
  • 至于这个为什么呢?我也没有想明白这些,之后再努力看看才行。
int (*f)() = []() -> int {return 1;};
int (*g)() = []() -> int {return 1;};

int a(){return 1;}
int (*h)() = a; // 也可写做 int(*h)() = &a; 因为函数名本来就是地址