C++——lambda表达式

介绍

lambda表达式是一种局部类类型,它含有一个构造函数,和一个const成员函数operator()()。

lambda表达式除了能做参数外,还能用于初始化一个声明为auto或者std::function<R(LA)>的变量。R是lambda的返回类型,LA是它的类型参数列表。

组成部件

  1. 一个可能为空的捕获列表,指明定义环境中哪些名字能够被用在lambda表达式中,以及这些名字是被拷贝还是引用。捕获列表位于[]中。 补充: 静态变量不能也不需要被捕获。
  2. 一个可选的参数列表。指明所需要的参数,位于()中。补充 当无参数时,可以省略。
  3. 一个可选的mutable修饰符,当需要修改通过值捕获的变量的副本时,位于()之后。
  4. 一个可选的noexcept修饰符,表明无异常被抛出。位于mutable修饰符之后。
  5. 一个可选的->形式的返回类型声明,可与decltype一起搭配使用。
  6. 一个表达式体,指明要执行的代码,位于{}块中。

实现模型

对于lambda表达式而言,有着很多种表述方法,并且有着多中优化途径。如果把lambda表达式看成是一种定义并使用函数对象的便捷方式,那么便于理解。

假设有这么一个例子:

void print_modulo(const vector<int>& v, ostream& os, int m)
{for_each(begin(v), end(v),[&os, m](int x) {if (x % m == 0) os << x << '/n';});
}

我们可以将其中的lambda表达式改写成一个函数对象:

class Modulo_print
{ostream& os;int m;
public :Modulo_print(ostream& s, int mm) :os{ s }, m{ mm } {}void operator() (int x) const {if (x % m == 0) os << x << '/n';}
};

并且改写之前的例子:

void print_modulo(const vector<int>& v, ostream& os, int m)
{for_each(begin(v), end(v),Modulo_print{os, m});
}

我们把由lambda表达式生成的函数对象称为闭包对象 (闭包)

如果lambda表达式通过引用 ([&]) 捕获它定义环境中的每一个局部变量,则其闭包对象则可以优化为简单的包含一个指向外层栈框架的指针。

捕获

lambda表达式的主要用途是封装一部分代码以便将其用做参数。

有些lambda表达式无需访问它的局部环境,这样lambda表达式使用空引入符 [ ] 定义。

lambda引入符的形式有很多:

  1. [ ]空捕获列表,在lambda表达式内部无法使用其外层上下文中的任何局部名字,其数据需要从实参或者非局部变量中获得。
  2. [&]通过引用隐式捕获。所有局部变量通过引用使用。
  3. [=]通过值隐式捕获。所有局部变量通过拷贝使用,这些副本是在lambda表达式的调用点获得的。
  4. [捕获列表] 显式捕获。通过引用或者值方式捕获其局部变量。捕获列表中可以出现this (通过值方式捕获) 和 紧跟…的名字以表示的元素。
  5. [&, 捕获列表] 对于出现在捕获列表中的名字以值方式捕获,不能以&为名字前缀。捕获列表可以出现this。
  6. [=, 捕获列表] 对于出现在捕获列表中的名字以引用方式捕获,必须以&为前缀,捕获列表中不能出现this。

对于lambda表达式来说,当把lambda传递给另一个线程时,用值捕获更优,通过引用或者指针访问其他线程中的内容是一种危险操作,对于性能和正确性来说都是如此。

对于可变模板参数的捕获示例:

template <typename...Var>
void algo(int s, Var... var)
{
auto helper = [&s, &var...]{return s * (h1(var...) + h2(var...)); };
}

生命周期

lambda表达式的生命周期可能比它的被调用者更长。当我们把lambda表达式传递给另一个线程或者存在别处以供后续使用的时候。
例如:

void setup(Menu& m)
{
Point p1, p2, p3;
m.add("draw a triangle", [&]{m.draw(p1, p2, p3); }); //可能会发生程序错误
}

当setup完成之后,我们可能需要画一个三角型,此时lambda表达式将会访问一个已经不存在的局部变量。

如果我们发现一个lambda表达式的生命周期可能比它的调用者更长,则我们需要确保所有局部信息都已经被拷贝到闭包对象中去。

m.add("draw a triangle", [=]{m.draw(p1, p2, p3); });

lambda与this

当lambda表达式被用在成员函数当中,我们怎样去访问类成员变量呢?我们通过捕获this指针来访问类成员对象。
例如:

template <typename T>
class Request
{typedef typename vector<T>::value_type result;function<result(const vector<T>&)> oper;vector<T> values;result results;
public:void execute(){[this] {results = oper(values);};}
};

类成员变量是通过this访问的,在lambda表达式中[this] 和 [=] 互不兼容,因此一不小心就可能在多线程程序中造成竞争条件。

mutable的lambda表达式

如果希望在lambda表达式修改函数对象的状态,即修改通过值捕获的变量,则可以使用mutable修饰符:

void algo()
{int count = 100;[count]()mutable {return --count;};
}

lambda表达式与返回类型

lambda表达式的大多数规则都是从类和函数借鉴过来的,然而需要有两点需要注意:

  1. 如果lambda表达式不需要任何参数,则参数列表可以省略。因此lambda表达式的最简形式是[]{}。
  2. lambda表达式的返回类型可以有其本身推断得到,而函数(方法)不行。

如果在一条lambda表达式的主体部分不包含return语句,则其返回类型为void。
如果lambda表达式包含一条return语句,则返回类型是return表达式的类型。 其他情况,必须显式定义一个返回类型。

lambda表达式的类型

任意两个lambda表达式的类型都不相同,一旦两个lambda表达式的类型相同,则模板实例化机制就无法识别它们了。

例如:

auto algo = [&algo](char* b, char* a) {swap(*b, *--a); algo(++b, a);};

由于algo的类型并没有在使用之前被推断出来,因此上面的写法是错误的。

使用function包装器可以存入函数对象。就可以保证,在使用之前, algo的类型就已经知道了。

	function<void (char*b, char*a)>  algo = [&](char* b, char* a) {swap(*b, *--a); algo(++b, a);};

如果我们只是想给lambda表达式起个名字,而不会在主体内部递归的使用它。则:

auto algo = [&](char* b, char* a) {swap(*b, *--a); };

是没有问题的。

如果lambda表达式什么也不捕获,则我们可以把它赋值给一个指向正确类型的函数指针。

	double (*fun) (double a) = [](double a) {return sqrt(a);};

但行好事, 莫问前程!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246302.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GPU Gems1 - 15 逐像素光照的可见性管理

这章讲到了可见性在逐像素渲染光照场景中的作用&#xff0c;也考虑如何使用可见性减少必须渲染的批次数量&#xff0c;从而改善性能。 假设一个非常简单的场景&#xff0c;一个房间&#xff0c;因为房间的不同部位是不同的材质&#xff0c;所以将其分为8个batch&#xff0c;另…

重建世界坐标

1.概论 在屏幕空间做一些操作的时候往往需要从屏幕坐标和深度纹理中重建出相应的世界坐标。 本文会讨论两种重建世界坐标的方法&#xff0c;并详细讲解第二种方法。 使用VP逆矩阵CameraPosInWorld linearDepth * Direction 2.使用VP逆矩阵法 我们知道屏幕坐标是通过Object…

GPU Gems1 - 16 次表面散射的实时近似(Real-Time Approximations to Subsurface Scattering)

这一章&#xff0c;浅墨这篇文章介绍的非常详细&#xff0c;我照扒一遍再稍加一些内容&#xff0c;增加记忆。https://zhuanlan.zhihu.com/p/36499291 【章节概览】 次表面散射&#xff08;Subsurface Scattering&#xff09;&#xff0c;简称SSS&#xff0c;或3S&#xff0c…

Unity HDRP渲染管线基础指南

HDRP和LWRP简介 Unity2018中引入了可编程渲染管线&#xff08;Scriptable Render Pipeline&#xff0c;简称SRP&#xff09;&#xff0c;是一种在Unity中通过C#脚本配置和执行渲染的方式。至2018.1版本&#xff0c;Unity中除了默认渲染管线&#xff0c;还提供了轻量级渲染管线…

SSDO AO 图像空间的动态全局光照

本文仅记述SSDO学习笔记&#xff01; 概述 SSDO Screen-space Direction Occlusion 是对AO的扩充&#xff0c;在AO的基础上&#xff0c;假设没有遮蔽的方向有直接入射光&#xff0c;有遮蔽的方向&#xff0c;可能有间接光的反射。 SSAO理论分析 AO&#xff1a;在计算点的周围…

对于C++的思考

前言 本文是Bjarne Stroustrup的HOPL4论文 笔记及拓展(目录) 的起始篇。 C的理念 C被设计出来到底是为了回答什么样的问题呢&#xff1f; 如何直接操纵硬件&#xff1f;同时支持高效&#xff0c;高级的抽象&#xff1f; C在1980年代仅仅是一个基于C和Simula语言功能的组合…

GPU Gems1 - 17 环境遮挡

环境光遮蔽&#xff08;Ambient Occlusion&#xff09;“AO”为Amblent Occlusion的缩写&#xff0c;中文译为环境光遮蔽。在DirectX 10.1 API推出后&#xff0c;Amblent Occlusion升级为SSAO&#xff08;Screen-Space Ambient Occlusion:屏幕空间环境光遮蔽&#xff09;&#…

C++ 模板浅谈

引言与概述 C模板机制允许在定义类&#xff0c;函数&#xff0c;类型别名的时候将类型或值当作参数&#xff0c;这样定义的类和函数在运行时间和空间效率上并不逊色于手工打造的非通用的代码。 模板提供的代码是类型安全的。 模板是一种编译时期的机制&#xff0c;与手工编写…

GPU Gems1 - 18 空间BRDF(Spatial BRDFs)

这章主要介绍了空间双向反射分布函数&#xff08;SBRDF&#xff09;&#xff0c;接着文章讨论了压缩SBRDF表达式&#xff0c;以及由离散光或环境贴图所照明的SBRDF的渲染方法。关于BRDF请参考大佬浅墨的这篇文章https://blog.csdn.net/poem_qianmo/article/details/75943714 S…

浅谈表面反射——波动光学篇

Radiometric Definitions 首先让我们来回忆一下辐射度量学中关于irradiance和radiance的定义&#xff1a; 我们在这里定义光源(source)在x-z平面&#xff0c;因此入射光就可以只用θi\theta_{i}θi​来描述。dAs\mathrm{d}A_{s}dAs​是表面积。定义dΦi\mathrm{d}\Phi_{i}dΦ…

GPU Gems1 - 19 基于图像的光照(Image-Based Lighting)

这篇文章打破了当时立方体贴图环境&#xff08;Cube-Map Environment&#xff09;用法的桎梏&#xff0c;深入研究了更多可能的逼真光照效果。文章主要研究了基于图像的光照&#xff08;Image-Based Lighting&#xff0c;IBL&#xff09;&#xff0c;包括局部化的立方体映射&am…

Polygonal-Light Shading with LTC

概述 如果我们有一个上半球的余弦分布函数(Do)(D_{o})(Do​)&#xff0c;并对这个余弦分布函数进行一个矩阵(M)(M)(M)变换&#xff0c;变换为一个新的分布函数(D)(D)(D)。 DMDoD M D_{o}DMDo​ 这就是基本思想&#xff1a;用一个余弦分布函数来拟合出不同的分布函数&#xff…

GPU Gems1 - 20 纹理爆炸(Texture Bombing)

【章节概览】 这章介绍了纹理爆炸&#xff08;Texture Bombing&#xff09;和相关的细胞技术&#xff0c;它们能在Shader中增加视觉的丰富性&#xff0c;图像的多样性&#xff0c;并减少大块纹理图案的重复性。 【核心要点】 纹理爆炸&#xff08;Texture bombing&#xff0…

Catlike Coding网站文章解析 -- 1.Procedural Grid

原文英文版链接https://catlikecoding.com/unity/tutorials/procedural-grid/&#xff0c;里面有每一部分的untiy工程链接&#xff0c;文章内容也更详实。 本章内容&#xff1a; 创建一系列点使用协程实现他们的摆放位置定义一个由三角形组成的平面自动生成法线添加纹理坐标和…

GPU Gems1 - 21 实时辉光(Real-Time Glow)

这章浅墨的文章讲的很细了&#xff0c;这里基本照扒一遍加深印象&#xff0c;原文链接&#xff1a;https://zhuanlan.zhihu.com/p/36499291 【章节概览】 这章讲到2D光照效果中的辉光&#xff08;Glow&#xff09;和光晕&#xff08;Halo&#xff09;&#xff0c;展示了如何通…

Catlike Coding网站文章解析 -- 2.Procedural Grid

本章内容 创建一个闭合的cube mesh给cube添加带弧度平滑的边缘定义法线使用sub-meshes&#xff08;子mesh&#xff09;创建一个常规shader合并碰撞体1.合成一个cube 上一章https://mp.csdn.net/postedit/89474068我们已经实现了一个平面mesh。一个cube由6个平面组成&#xff…

GPU Gems1 - 23 景深技术综述

本章主要介绍如何使用GPU创建实时的景深&#xff08;Depth of Field&#xff09;效果。参考浅墨总结文章的链接https://zhuanlan.zhihu.com/p/36499291 聚焦在枪上&#xff0c;背景模糊【核心要点】 物体在距离镜头的一个范围之内能够清晰成像&#xff08;经过聚焦&#xff09…

GPU Gems1 - 22 颜色控制(Color Controls)

这章将在游戏中图像处理的讨论&#xff0c;扩展到技术和艺术上控制颜色的方法和应用&#xff0c;包括将图像从一些的色彩空间中移入移出&#xff0c;以及快速地给任何2D或3D场景加上精美的色调。 色彩校正&#xff08;Color Correction&#xff09;是几乎所有印刷和胶片成像应…

GPU Gems1 - 25 用纹理贴图进行快速过滤宽度的计算

这章介绍在片元shader中计算导数近似值的技术。这个技术把特别的值载入纹理mipmap的每个级别&#xff0c;以特殊的方式使用纹理映射硬件&#xff0c;计算出导数的结果&#xff0c;给出在屏幕上的像素间某个量的变化率。 Cg标准程序库提供ddx和ddy函数&#xff0c;计算任意关于…

c#基础知识(1)

简介&#xff1a;C# 是一个现代的、通用的、面向对象的编程语言&#xff0c;它是由微软&#xff08;Microsoft&#xff09;开发的&#xff0c;由 Ecma 和 ISO 核准认可的。 程序结构&#xff1a;引用命名空间、命名空间、类、方法、属性、语句。 数据类型&#xff1a;bool、byt…