阅读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)...));
}

匿名函数 lambda表达式(lambda expression)的更多相关文章

  1. 委托、匿名委托、Lambda 表达式、Expression表达式树之刨根问底

    本篇不是对标题所述之概念的入门文章,重点在阐述它们的异同点和应用场景.各位看官,这里就不啰嗦了,直接上代码. 首先定义一个泛型委托类型,如下: public delegate T Function&l ...

  2. 转载 C#匿名函数 委托和Lambda表达式

    转载原出处: http://blog.csdn.net/honantic/article/details/46331875 匿名函数 匿名函数(Anonymous Function)是表示“内联”方法 ...

  3. 闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别

    闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别 函数最常见的形式是具名函数(named function): function foo(){ con ...

  4. Lambda表达式(lambda expression)⭐⭐⭐⭐⭐

    原作者 lambda表达式(lambda expression)实际上是匿名函数一种表示形式, 即没有函数名的函数:参数列表=>表达式或语句块,在我看来主要目是为了简化代码编写,提高代码可读性而 ...

  5. c#中匿名函数lamb表达式

    c#中匿名函数lamb表达式 实例一:(其实,这样都是些语法糖) using System; using System.Collections.Generic; using System.Linq; ...

  6. C#中Lambda表达式类型Expression不接受lambda函数

    在EF Core中我们经常会用System.Linq.Expressions系统命名空间的Expression<TDelegate>类型来作为EF Core的查询条件,比如: using ...

  7. Lambda表达式 和 Expression<T>

    经常用的Lambda表达式 返回的是  Expression<T> 格式 public class ActivityService { private readonly List<A ...

  8. JAVA 8 Lambda表达式-Lambda Expressions

    Lambda表达式介绍 Lambda表达式是在java规范提案JSR 335中定义的,Java 8 中引入了Lambda表达式,并被认为是Java 8最大的新特性,Lambda表达式促进了函数式编程, ...

  9. Java基础进阶:内部类lambda重点摘要,详细讲解成员内部类,局部内部类,匿名内部类,Lambda表达式,Lambda表达式和匿名内部类的区别,附重难点,代码实现源码,课堂笔记,课后扩展及答案

    内部类lambda重点摘要 内部类特点: 内部类可以直接访问外部类,包括私有 外部类访问内部类必须创建对象 创建内部对象格式: 外部类.内部类 对象名=new外部类().new内部类(); 静态内部类 ...

  10. 深入学习C#匿名函数、委托、Lambda表达式、表达式树类型——Expression tree types

    匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...

随机推荐

  1. Asp.net Core基于MVC框架实现PostgreSQL操作

    简单介绍 Asp.net Core最大的价值在于跨平台.跨平台.跨平台.重要的事情说三遍.但是目前毕竟是在开发初期,虽然推出了1.0.0 正式版,但是其实好多功能还没有完善.比方说编译时的一些文件编码 ...

  2. git Could not read from remote repository 解决

    错误: fatal: 'origin' does not appear to be a git repository fatal: Could not read from remote reposit ...

  3. 关于C#的委托(delegate)的自我理解

    首先描述一个事情,一个老师饿了,他要去买东西填饱肚子,然后他发现他的学生“小李”在玩,没学习,于是就委托“小李”去帮他买吃的. 根据这件事我们来分析: 首先得有个老师(老师饿了是他的方法,老师买东西也 ...

  4. javascript Windouw 转自 http://www.cnblogs.com/kissdodog/archive/2013/01/01/2841464.html

    javascript之window对象 window :window对象是BOM中所有对象的核心,除了是BOM中所有对象的父对象外,还包含一些窗口控制函数. 1.全局的window对象 JavaScr ...

  5. FileInputStream、FileReader、FileInputStream、FileWriter使用小结

    本文是基于Linux环境运行,读者阅读前需要具备一定Linux知识 InputStream包含如下三个方法: int read():从输入流中读取单个字节,返回所读取的字节数据(字节数据可直接转化为i ...

  6. java泛型编译时被擦除引起多态的破坏,用 桥方法解决此类问题。(java 桥方法)

    在JVM虚拟机中泛型编译的时候,会出现类型擦除.但是,在多态场景中,编译时,擦除方式会出现多态被破坏的可能. 举个栗子: A.java public class A<T> { void g ...

  7. VUE 入门基础(2)

    二,起步 引用方式可以使用  vue-cli <script src="https://unpkg.com/vue/dist/vue.js"></script&g ...

  8. NFS服务器原理

    NFS服务器   port:2049      NFS 为 Network FileSystem 的简称,它的目的就是想让不同的机器.不同的操作系统可以彼此分享个别的档案啦!目前在 Unix Like ...

  9. (转)深入理解javascript连续赋值表达式

    引入 今天逛园子的时候看到一道javascript面试题,是关于连续赋值的,正好最近读jQuery源码经常看到这种连续赋值的表达式,所以很感兴趣. 废话不多说,来看题: var a = {n: 1} ...

  10. C# ADO.NET (sql语句连接方式)(增,删,改)

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...