• 匿名函数 lambda表达式(lambda expression)


    阅读g2log时,发现有两行代码居然看不懂。

    1. auto bg_call =  [this, log_directory]() {return pimpl_->backgroundChangeLogFile(log_directory);};

    2. auto bg_call = [&]() {return pimpl_->backgroundFileName();};

    https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0#C.2B.2B 中有所描述

    C++ 98/03

    C++ 98/03标准并不原生支持匿名函数。不过可以利用Boost库的Boost.Lambda来实现一个匿名函数。

    C++ 11

    C++11标准提供了匿名函数的支持,在《ISO/IEC 14882:2011》(C++11标准文档)中叫做lambda表达式。一个lambda表达式有如下的形式:

    [capture] (parameters) mutable exception attribute -> return_type { body }
    

    必须用方括号括起来的capture列表来开始一个lambda表达式的定义。

    lambda函数的形参表比普通函数的形参表多了3条限制:

    1. 参数不能有缺省值
    2. 不能有可变长参数列表
    3. 不能有无名参数

    如果lambda函数没有形参且没有mutable、exception或attribute声明,那么参数的空圆括号可以省略。但如果需要给出mutable、exception或attribute声明,那么参数即使为空,圆括号也不能省略。

    如果函数体只有一个return语句,或者返回值类型为void,那么返回值类型声明可以被省略:

    [capture](parameters){body}

    一个lambda函数的例子如下:

    [](int x, int y) { return x + y; } // 從return語句中隱式獲得的返回值類型
    [](int& x) { ++x; }   // 沒有return語句 -> lambda函數的返回值為void
    []() { ++global_x; }  // 沒有參數,僅僅是訪問一個全局變量
    []{ ++global_x; }     // 與前者相同,()可以被省略

    在上面的第一个例子中这个无名函数的返回值是decltype(x+y)。如果lambda函数体的形式是return expression,或者什么也没返回,或者所有返回语句用decltype都能检测到同一类型,那么返回值类型可以被省略。

    返回值类型可以显式指定,如下所示:

    [](int x, int y) -> int { int z = x + y; return z; }

    在这个例子中,一个临时变量,z,被创建来存储中间过程。与一般的函数一样,中间值在调用的前后并不存在。什么也没有返回的lambda表达式无需显式指定返回值,没有必要写-> void代码。

    lambda函数可以捕获lambda函数外的具有automatic storage duration的变量,即函数的局部变量与函数形参变量。函数体与这些变量的集合合起来称做闭包。这些外部变量在声明lambda表达式时列在在方括号[]中。空的方括号表示没有外界变量被capture或者按照默认方式捕获外界变量。这些变量被传值捕获或者引用捕获。对于传值捕获的变量,默认为只读(这是由于lambda表达式生成的为一个函数对象,它的operator()成员缺省有const属性)。修改这些传值捕获变量将导致编译报错。但在lambda表达式的参数表的圆括号后面使用mutable关键字,就允许lambda函数体内的语句修改传值捕获变量,这些修改与lambda表达式(实际上是用函数对象实现)有相同的生命期,但不影响被传值捕获的外部变量的值。lambda函数可以直接使用具有static存储期的变量。如果在lambda函数的捕获列表中给出了static存储期的变量,编译时会给出警告,仍然按照lambda函数直接使用这些外部变量来处理。因此具有static存储期的变量即使被声明为传值捕获,修改该变量实际上直接修改了这些外部变量。编译器生成lambda函数对应的函数对象时,不会用函数对象的数据成员来保持被“捕获”的static存储期的变量。示例:

    []        // 沒有定義任何變量,但必须列出空的方括号。在Lambda表達式中嘗試使用任何外部變量都會導致編譯錯誤。
    [x, &y]   // x是按值傳遞,y是按引用傳遞
    [&]       // 任何被使用到的外部變量都按引用傳入。
    [=]       // 任何被使用到的外部變量都按值傳入。
    [&, x]    // x按值傳入。其它變量按引用傳入。
    [=, &z]   // z按引用傳入。其它變量按值傳入。

    下面这个例子展示了lambda表达式的使用:

    std::vector<int> some_list{ 1, 2, 3, 4, 5 };
    int total = 0;
    std::for_each(begin(some_list), end(some_list), [&total](int x) {  total += x; } );
    在类的非静态成员函数中定义的lambda表达式可以显式或隐式捕捉this指针,从而可以引用所在类对象的数据成员与函数成员。

    lambda函数的函数体中,可以访问下述变量:

    • 函数参数
    • 局部声明的变量
    • 类数据成员(当lambda表达式声明在类中),需要注意的是实际上捕获了对象的this指针。
    • 具有静态存储期的变量(如全局变量)
    • 被捕获的外部变量
      • 显式捕获的变量
      • 隐式捕获的变量,使用默认捕获模式(传值或引用)来访问。

    lambda函数的数据类型是函数对象,保存时必须用std::function模板类型或auto关键字。 例如:

    #include <functional>
    #include <vector>
    #include <iostream>
    
    double eval(std::function <double(double)> f, double x = 2.0)
    {
    	return f(x);
    }
    
    int main()
    {
    	std::function<double(double)> f0    = [](double x){return 1;};
    	auto                          f1    = [](double x){return x;};
    	decltype(f0)                  fa[3] = {f0,f1,[](double x){return x*x;}};
    	std::vector<decltype(f0)>     fv    = {f0,f1};
    	fv.push_back                  ([](double x){return x*x;});
    	for(int i=0;i<fv.size();i++)
    		std::cout << fv[i](2.0) << std::endl;
    	for(int i=0;i<3;i++)
    		std::cout << fa[i](2.0) << std::endl;
    	for(auto &f : fv)
    		std::cout << f(2.0) << std::endl;
    	for(auto &f : fa)
    		std::cout << f(2.0) << std::endl;
    	std::cout << eval(f0) << std::endl;
    	std::cout << eval(f1) << std::endl;
    	std::cout << eval([](double x){return x*x;}) << std::endl;
    	return 0;
    }

    一个lambda函数的捕捉表达式为空,则可以用普通函数指针存储或调用。例如:

    auto a_lambda_func = [](int x) { /*...*/ };
    void (* func_ptr)(int) = a_lambda_func;
    func_ptr(4); //calls the lambda.

    C++14

    C++14增加了初始捕获(init capture)。由于lambda表达式实际构造了一个函数对象,初始捕获实质是初始化了函数对象的成员。C++14还允许lambda函数的形参使用auto关键字作为其类型,这实质上是函数对象的operator()成员作为模板函数;并且允许可变参数模板

    auto a_lambda_func = [data1=101](int x) { /*...*/ }; //初始捕获,实质上是在函数对象增加了数据成员data1并初始化
    
    auto ptr = std::make_unique<int>(10); //See below for std::make_unique
    auto lambda1 = [ptr = std::move(ptr)] {return *ptr;}
              //大致等效于:   
    auto lambda2 = [ptr = std::make_unique<int>(10)] {return *ptr;}
    
    auto lambda3 = [](auto x, auto y) {return x + y;} //lambda函数的形参类型为auto
    struct unnamed_lambda                            //这相当于函数对象:
    {
      template<typename T, typename U>
        auto operator()(T x, U y) const {return x + y;}
    };
    
    auto lambda4 = [](auto&&... params)             //可变参数的函数模板
    {
         return (foo(std::forward<decltype(params)>(params)...));
    }
  • 相关阅读:
    用例失败重新运行
    pytest启动浏览器,失败用例截图
    解决pycharm问题:module 'pip' has no attribute 'main'
    pytest的HTML
    pytest 的 yield
    pytest的setup和teardown
    pytest的fixture和conftest
    pycharm运行pytest
    简单易用的MongoDB
    快速入门系列--CLR--02多线程
  • 原文地址:https://www.cnblogs.com/liaokang/p/5773037.html
Copyright © 2020-2023  润新知