assert函数_一文带你认识 C++ 中的 Lambda 函数

(给CPP开发者加星标,提升C/C++技能)

英文:Vishal Chovatiya,翻译:CPP开发者/张宗豪

【导读】:Lambda函数是 Modern C++在C++11中的一个体现 ,虽然在网络上已经有很多关于lambda函数的讨论或者教学,但是仍然有一部分内容(例如IIFE,lambda类型等)没人谈论。因此在这里,我不仅要向您展示C++中的lambda函数,而且还将介绍Lambda的工作原理以及发展。

以下是正文


什么是lambda函数?

本文标题有点误导。因为 lambda并不总是函数指针,它一个表达式。但是为了简单起见,我一直都称它为函数。那么在本文中,我将会交替使用lambda函数和表达式来称呼。

Lambda函数是一段简短的代码片段,它具有以下特点:

(1)不值得命名(无名,匿名,可处理等,无论您如何称呼)

(2)不会重复使用

换句话说,它只是语法糖。lambda函数的语法定义为:

[ capture list ] (parameters) -> return-type {      method definition}

通常, 编译器会评估lambda函数本身的返回类型。因此,我们不需要显式指定返回类型,即 -> return-type,但是在某些复杂的情况下,编译器无法推断出返回类型,此时我们需要指定返回类型。

为什么要使用lambda函数?

 C++包括许多有用的通用函数,例如 std::for_each,通常情况下方便了我们的使用。但是,当需要考虑特殊需求的时候,就比较头疼了。如下代码所示:

struct print{    void operator()(int element){        cout <endl;    }};int main(void){    std::vector<int> v = {1, 2, 3, 4, 5};    std::for_each(v.begin(),v.end(), print());    return 0;}

如果您只在某个特定位置使用一次print,却写了一个类。这样的操作代价似乎太大了。

但是,对于这种情况,内联代码将更合适,并且可以通过如下的lambda函数来实现:

std::for_each(v.begin(), v.end(), [](int element) { cout <endl; });

lambda函数工作原理

[&i] ( ) { std::cout << i; }// is equivalent tostruct anonymous{    int &m_i;    anonymous(int &i) :m_i(i) {}    inline autooperator()()const{        std::cout <    }};

如代码所示,编译器为每一个lambda函数生成独特的闭合。捕获列表将成为闭包中的构造函数参数,如果按值捕获,则会在闭包中创建相应类型的数据成员。此外,您可以在lambda函数参数中声明变量/对象,它将成为调用运算符的参数,即operator()。

使用Lambda函数的好处

(1)零成本抽象。是的!  你没看错。lambda不会牺牲性能,运行速度和普通函数一样快。

(2)此外,代码会变得更加紧凑,结构层次更加明显和代码可读性更佳。

学习lambda表达式

1.通过引用/值捕获

int main(){    int x = 100, y = 200;    auto print = [&]{ // Capturing object by reference        std::cout <<__pretty_function__>" : " << x<<     };    print();    return 0;}

输出:

main()::()> : 100 , 200

在上面的示例中,我在捕获列表中用到了 &。通过引用的方式捕获变量 x和 y。同样,= 表示按值捕获,这将在闭包内创建相同类型的数据成员,同时赋上相同的值。需要注意的是,参数列表是可选的, 如果不将参数传递给lambda表达式,则可以省略空括号。

2.Lambda捕获列表

2.1 将lambda作为参数传递

template <typename Functor>void f(Functor functor){    std::cout <<__pretty_function__>std::}/* Or alternatively you can use thisvoid f(std::function functor){    std::cout <<__pretty_function__ std::endl>}*/int g() { static int i = 0; return i++; }int main(){    auto lambda_func =[i = 0]() mutable { return i++; };    f(lambda_func); // Pass lambda    f(g);           // Pass function}

输出:

Function Type : void f(Functor) [with Functor = main()::int)>]Function Type : void f(Functor) [with Functor = int (*)(int)]

您也可以将lambda函数作为参数传递给其他函数,就像我上面编写的普通函数一样。相信您注意到了,我在捕获列表中声明了变量i,它将成为数据成员。所以,每次调用lambda_func时,它将被返回并递增。

2.2lambda捕获this指针或成员变量

class Example{public:    Example() : m_var(10) {}    void func(){        [=]() { std::cout << m_var<< std::endl; }(); // IIFE    }private:    int m_var;};int main(){    Example e;    e.func();}

捕获this指针也可以使用 [this], [=]或者 [&]。在上述任何情况下,类内数据成员(包括 private)的访问方式与常规方法一样。

可以看到lambda表达式的末尾,我多写了一个 ( ),该函数通常在声明之后立刻对其进行调用。它称为IIFE(立即调用函数表达式)。

C++ lambda函数类型

1.通用lambda

const auto l = [](auto a, auto b, auto c) {};// is equivalent tostruct anonymous{    template <class T0, class T1, class T2>    auto operator()(T0 a, T1 b, T2 c) const    {    }};

C++ 14中引入的通用lambda可以使用auto说明符。

2.可变参数通用λ

void print() {}template <typename First, typename... Rest>void print(const First &first, Rest &&... args){    std::cout << first<< std::endl;    print(args...);}int main(){    auto variadic_generic_lambda = [](auto... param) {        print(param...);    };    variadic_generic_lambda(1, "lol", 1.1);}

具有可变参数的Lambda在许多情况下非常有用,例如调试,使用不同的数据输入重复操作等。

3.mutable lambda函数

通常,lambda的函数调用运算符是const-by-value,这意味着lambda需要捕获可变值的 关键字时,需要使用mutable关键字。

[]() mutable {}// is equivalenttostruct anonymous{    auto operator()()  // call operator{    }};

4.Lambda作为函数指针

#include#includeint main(){      auto funcPtr = +[] {};      static_assert(std::is_same<decltype(funcPtr), void (*)()>::value);}

您可以通过添加命令“+”来强制编译器将lambda生成为函数指针而不是闭包。

5.lambda函数作为返回值

const auto less_than = [](auto x) {    return [x](auto y) {        return y < x;    };};int main(void){    auto less_than_five = less_than(5);    std::cout << less_than_five(3) << std::endl;    std::cout << less_than_five(10) << std::endl;    return 0;}

再进一步,lambda函数还可以返回另一个lambda函数。这也为代码的自定义性,代码可读性和紧凑性带来无限可能。

6.constexpr lambda表达式

从C ++ 17开始,可以将lambda表达式声明为 constexpr。

constexpr auto sum = [](const auto &a, const auto &b) { return a + b; };/*    is equivalent to    constexpr struct anonymous    {        template         constexpr auto operator()(T1 a, T2 b)const        {            return a + b;        }    };*/constexpr int answer = sum(10, 10);

即使你没有指定 constexpr,如果它恰好满足所有 constexpr函数的要求,那么它也会被声明为constexpr。

结束语

希望您喜欢这篇文章。文中我使用几个简单的小例子来介绍关于lambda的大量复杂问题。考虑到代码的可表达性和易维护性,无论您想到什么,都应该使用lambda,就像可以在自定义删除器中将其用于智能指针和大多数STL算法一样。

https://hackernoon.com/all-about-lambda-functions-in-cfrom-c11-to-c17-2t1j32qw

- EOF -

推荐阅读   点击标题可跳转

1、C++仿函数了解一下?

2、C++11 的异步操作不只是 thread

3、C++ 性能优化之性能刺客

关于C++lambda,欢迎在评论中和我探讨。觉得文章不错,请点赞和在看支持我继续分享好文。谢谢!

关注『CPP开发者』

看精选C++技术文章 . 加C++开发者专属圈子

↓↓↓

036c9890f84a9d8b8733c4c2b064d706.png

点赞和在看就是最大的支持❤️

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值