0
点赞
收藏
分享

微信扫一扫

C/C++语言基础--C++“神奇”,Lambda表达式

本专栏目的

  • 更新C/C++的基础语法,包括C++的一些新特性

前言

  • C++11中Lambda表达式是一个很有用的特性,在我的学习中,用处很大,这一篇是对Lambda的一个小总结;
  • C语言后面也会继续更新知识点,如内联汇编;
  • 欢迎收藏 + 关注,本人将会持续更新。

文章目录

lambda表达式

概念:Lambda表达式是现代C++在C ++ 11和更高版本中的一个新的语法糖 ,lambda表达式(也称为lambda函数)是在调用或作为函数参数传递的位置处定义匿名函数对象的便捷方法。通常,lambda用于封装传递给算法或异步方法的几行代码 。

🚄 简单理解:Lambda就是,形势简单,使用简单,使用范围广的函数。

Lambda表达式语法

语法如下:

[capture list](parameters)mutable noexcept ->return type
{
    statement;
}
  • **捕获列表(capture list):**捕获列表能够捕捉上下文中的变量以供Lambda函数使用。
  • parameters:参数列表
  • **可变的(mutable):**可以变的,和const是反义词。默认情况下Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)。
  • **异常说明(noexcept):**用于Lamdba表达式内部函数是否可以抛出异常。
  • **返回类型(return type):**追踪返回类型(也叫尾拖返回类型)形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。
  • **函数体(statement):**内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。

重点参数:

  • 捕获列表、参数列表、函数体、返回值类型(他也会自动推断)

闭包

闭包概念

闭包函数:声明在函数中的函数,叫做闭包函数

闭包:内部函数总是可以访问外部函数的变量,即使在外部函数声明周期结束后

🎫 提示: 在C++11前,C++是不支持闭包的,在C++11后,可以用Lambda、bind语法来实现闭包效果。

js代码演示代码如下(不用java是因为本人不熟悉🤙)

function out() {
    let a = 10, b = 20;
    
    function inner() {
        console.log(a, b)
    }

    inner()
}

out()

输出:

[Running] node "f:\DeepLearn\test.js"
10 20

Lambda捕获列表详解

Lambda表达式与普通函数最大的区别是,除了可以使用参数以外,Lambda函数可以通过捕获列表访问一些上下文中的数据,这也是Lambda的最核心的东西。

  • [ ]中没有任何捕获,表示不捕获任何外部变量

    auto function = ([]{
    		std::cout << "Hello World!" << std::endl;
    	}
    );
    
    function();
    
    /*
    输出:Hello World!
    */
    
  • [var]表示按值捕获指定的的变量var

    int num = 100;
    auto function = ([num]{       
    		std::cout << num << std::endl;
    	}
    );
    
    function();
    
    /*
    输出:100
    */
    
  • [=]表示值传递方式捕获所有父作用域的变量(包括this指针)

    int index = 1;
    int num = 100;
    auto function = ([=]{
    			std::cout << "index: "<< index << ", " 
                    << "num: "<< num << std::endl;
    	}
    );
    
    function();
    
    /*
    输出:index: 1,num: 100
    */
    
  • [&var]表示按引用捕获指定的变量var

    int num = 100;
    auto function = ([&num]{    //引用
    		num = 1000;
    		std::cout << "num: " << num << std::endl;
    	}
    );
    
    function();
    std::cout << "out num: " << num << std::endl;
    
    /*
    输出:
    num: 1000
    out num: 1000	
    */
    
  • [&]表示按引用捕获所有父作用域的变量(包括this)

    int index = 1;
    int num = 100;
    auto function = ([&]{    
    		num = 1000;
    		index = 2;
    		std::cout << "index: "<< index << ", " 
                << "num: "<< num << std::endl;
    	}
    );
    
    function();
    
    /*
    输出:index: 2,num: 1000
    */
    
  • [this]表示值传递方式捕获当前的this指针

    #include <iostream>
    using namespace std;
     
    class Lambda
    {
    public:
        void sayHello() {
            std::cout << "Hello" << std::endl;
        };
    
        void lambda() {
            auto function = [this]{ 
                this->sayHello(); 
            };
    
            function();
        }
    };
     
    int main()
    {
        Lambda demo;
        demo.lambda();
    }
    
    /*
    输出:Hello
    */
    

=、&混合搭配

  • [=,&a,&b]表示按引用捕获变量a和b,按值捕获其他所有变量

    int index = 1;
    int num = 100;
    auto function = ([=, &index, &num]{
    		num = 1000;
    		index = 2;
    		std::cout << "index: "<< index << ", " 
                << "num: "<< num << std::endl;
    	}
    );
    
    function();
    
    /*
    输出:index: 2, num: 1000
    */
    
  • [=,a]这里已经以值传递方式捕捉了所有变量,但是重复捕捉a了,会报错的;

  • [&,&this]这里&已经以引用传递方式捕捉了所有变量,再捕捉this也是一种重复。

Lambda参数列表

除了捕获列表之外,Lambda**还可以接受输入参数。**参数列表是可选的,并且在大多数方面类似于函数的参数列表。

auto function = [] (int first, int second){
    return first + second;
};
	
auto res = function(100, 200);
std::cout << res << std::endl;

/*
输出:300
*/

可变规格mutable

mutable修饰符, 默认情况下Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)。

#include <iostream>
using namespace std;

int main()
{
   int m = 0;
   int n = 0;
   [&, n] (int a) mutable { m = ++n + a; }(4);
   cout << m << endl << n << endl;
}

/*
输出:5 1
*/

异常说明

可以使用 throw() 异常规范来指示 Lambda 表达式不会引发任何异常,与普通函数一样,如果 Lambda 表达式声明 C4297 异常规范且 Lambda 体引发异常,Visual C++ 编译器将生成警告 throw()

int main() // C4297 expected 
{ 
 	[]() throw() { throw 5; }(); 
}

返回类型

Lambda表达式的返回类型会自动推导。除非你指定了返回类型,否则不必使用关键字,这一点我感觉是非常方便的。

Lamdba的一些使用场景

闭包实现

// js代码如下:
/*
function out() {
    let a = 10, b = 20;
    
    function inner() {
        console.log(a, b)
    }

    inner()
}

out()
*/

// 修改成C++代码
#include <iostream>

void out() {
    int a = 10, b = 20;

    auto inner = [a, b]() {
        std::cout << a << " " << b << std::endl;
    };

    inner();
} 

int main()
{
    out();

    return 0;
}

输出结果也是和js一样的。

STL中回调函数

⏰ STL函数中有很多参数是模板函数,什么是模板呢?这个我们后面呢会详细讲解,我们可以通过Lamdba直接在函数调用中实现效果,如下:

// 实现排序:递减

std::vector<int> num{3,5,1,2,6,88,111};

std::sort(num.begin(), num.end(), [](const int& a, const int& b) {
    return a > b;
});

for(auto i : num) {
    std::cout << i << " ";
}

/*
输出:
PS F:\DeepLearn\output> & .\'main.exe'
111 88 6 5 3 2 1 
*/

注意: 也可以充当其他函数的回调函数

递归

用Lamdba实现递归,这个相比于传统方法,在很多时候会简单很多,比如说在下面一道力扣,实现二叉树的后序遍历:

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        auto&& dfs = [&](auto&& dfs, TreeNode* root, vector<int>& res){
            if(root == nullptr) return;
            dfs(dfs, root->left, res);
            dfs(dfs, root->right, res);
            res.push_back(root->val);
        };

        dfs(dfs, root, res);

        return res;

    }
};

简化代码

这个应用场景非常多,Lambda我认为是C++的一项很伟大的特性,他在很多时候实现了代码简化,在很多时候非常方便,如下实现一个前端发送连错信息错误处理:

m_server.setMissingHandler([](const QHttpServerRequest& request,QHttpServerResponder&& responder) {
	QJsonObject jerr;
	jerr.insert("code: ", "3000");
	jerr.insert("message: ", "404^^^^");
	return responder.write(QJsonDocument(jerr));
	});

Lambda表达式的优缺点

优点

  1. 可以直接在需要调用函数的位置定义短小精悍的函数,而不需要预先定义好函数;
  2. 使用Lamdba表达式变得更加紧凑,结构层次更加明显、代码可读性更好。

缺点

  1. Lamdba表达式语法比较灵活,增加了阅读代码的难度;
  2. 对于函数复用无能为力(在不同的作用域中,无法复用);
  3. 如果代码多,不建议使用。

Lambda表达式工作原理

Lambda底层原理是利用仿函数,这个概念后面我们会讲解,在创建Lamdba后编译器会把一个Lambda表达式生成一个匿名类的匿名对象,并在类中重载函数调用运算符,实现了一个operator()方法,如:

auto print = []{cout << "Hello World!" << endl; };

编译器会把上面这一句翻译为下面的代码:

class Labmda_1
{
public:
	void operator()(void) const
	{
		cout << "Hello World!" << endl;
	}
};
// 用构造的类创建对象,print此时就是一个函数对象
auto print = print_class();
举报

相关推荐

lambda表达式c++

C++ lambda表达式

C++——lambda表达式

【C++】lambda表达式

C++ Lambda表达式基础用法

【C++】 Lambda表达式详解

C++ lambda表达式详解

C++特性-lambda表达式

0 条评论