24、【C++】C++11新特性:Lamda表达式/可变参数模板

2023-11-08

一、Lamda表达式

    Lamda表达式是C++11中引入的一项新技术,利用Lamda表达式可以编写内嵌的匿名函数,用以替换独立函数或者函数对象,并且使得代码更可读。是一种匿名函数,即没有函数名的函数;Lamda函数的语法定义如下:

[capture] :捕捉列表,捕捉列表总是作为lambda的开始,即出现于lambda的开始处。它是lambda的引出符(即开始标志)。编译器可以根据该“标志”来作出判断出该函数是否为lambda函数。同时“捕捉列表”能够捕捉上下文中的变量以作为lambda函数使用。

(parameters):参数列表。和C/C++中的普通函数参数意义一样。该部分是可选的,意味着如果我们不需要进行参数传递时,可以连同括号“()”一起省略掉

mutable:该关键字为一个修饰符。在默认的情况下,lambda函数总是返回一个const,而当我们在参数列表后面注明了“mutable”关键字之后,则可以取消其常量性质。若在lambda中使用了mutable修饰符,则“参数列表”是不可省略掉的(即使是参数为空)。

->return-type: 函数的返回值类型。和C/C++中的普通函数返回值类型的性质一样。主要目的是用来追踪lambda函数(有返回值情况下)的返回类型。若lambda函数不需要返回值,则可以直接将这部分省略掉

{statement}:函数体。在该函数体中,除了可以使用参数列表中的变量外,还可以使用所有捕获到的变量(即[capture] 中的变量)。
【示例】

    // 指明返回类型
    auto add = [](int a, int b) -> int { return a + b; };
    // 自动推断返回类型
    auto multiply = [](int a, int b) { return a * b; };

    int sum = add(2, 5);   // 输出:7
    int product = multiply(2, 5);  // 输出:10

Lamda表达式中“捕捉列表”详解

    C++11中的lambda函数,其中的“捕捉列表”是由0个或多个“捕捉项”组成,并以逗号“,”分隔。捕捉列表有如下几种形式:

    (1)[]默认不捕获任何变量;

    (2)[var]表示值传递方式捕捉变量var;

    (3)[=]表示值传递方式捕捉所有副作用域的变量(包括this);

    (4)[&var]表示引用传递捕捉所有变量var;

    (5)[&] 表示引用传递捕捉所有父作用域的比哪里(包括this);

    (6)[=, &x]表示以值捕获所有变量,当x例外,通过引用捕获;

    (7)[&, x]表示以引用捕获所有变量,但x例外,通过值捕获;

    (8)[this] 表示引用捕获当前对象(其实是复制指针);

    (9)[*this]表示通过值方式捕获当前对象;
【示例1】

#include <iostream> 
#include <string> 
#include <stdio.h> 

using namespace std; 

int main() 
{ 
    int a = 1,b =2, c =3; 
    auto retVal = [=,&a,&b]() mutable->int //父作用域内变量a、b以引用方式捕获,其余变量以值捕获方式捕获
    { 
        printf("inner c[%d]\n",c); 
        a = 10; 
        b = 20; 
        c = 30; 
        printf("inner c2[%d]\n",c); 
        return a+b; 
    }; 
    printf("sum[%d]\n",retVal()); 
    printf("a[%d] b[%d] c[%d]\n",a,b,c); 
    return 0; 
}

执行结果:

    inner c[3]
    inner c2[30]
    sum[30]
    a[10] b[20] c[3]

【示例2】

#include <iostream> 
#include <string> 
#include <stdio.h> 

using namespace std; 

int main() 
{ 
    int a = 1,b =2, c =3; 
    auto retVal = [&]() mutable->int //默认引用捕获方式捕获所有父作用域变量
    { 
        printf("inner a[%d] b[%d] c[%d]\n",a,b,c); 
        a = 10; 
        b = 20; 
        c = 30; 
        return a+b; 
    }; 
    printf("sum[%d]\n",retVal()); 
    printf("a[%d] b[%d] c[%d]\n",a,b,c); 
    return 0; 
}

执行结果:

    inner a[1] b[2] c[3]
    sum[30]
    a[10] b[20] c[30]

关于C++11中Lamda表达式更详尽的内容参见:https://www.cnblogs.com/Braveliu/p/4231818.html

二、可变参数模板

    在C++11之前,类模板和函数模板只能含有固定数量的模板参数。C++11增强了模板功能,允许模板定义中包含0到任意个模板参数,这就是可变参数模板

    可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号“…”:

template<class ... T> 
void func(T ... args)//T叫模板参数包,args叫函数参数包 
{
	//可变参数模板函数 

} 
func(); // OK:args不含有任何实参 
func(1); // OK:args含有一个实参:int 
func(2, 1.0); // OK:args含有两个实参int和double

1、可变参数模板函数

定义
    一个可变参数模板函数的定义如下:

#include <iostream> 

using namespace std; 

template<class ... T> 
void func(T ... args) 
{
    //可变参数模板函数 
    //sizeof...(sizeof后面有3个小点)计算变参个数 
    cout << "num = " << sizeof...(args) << endl; 
} 
int main() 
{ 
    func(); // num = 0 
    func(1); // num = 1 
    func(2, 1.0); // num = 2 
    return 0; 
}

执行结果:

    num = 0
    num = 1
    num = 2

参数包的展开
(1)递归方式展开
    通过递归函数展开参数包,需要提供一个参数包展开的函数和一个递归终止函数。

#include <iostream> 

using namespace std; 
//递归终止函数 
void debug() 
{ 
    cout << "empty\n"; 
} 
//展开函数 
template <class T, class ... Args> 
void debug(T first, Args ... last) 
{ 
    cout << "parameter " << first << endl; debug(last...); 
} 
int main() 
{ 
    debug(1, 2, 3, 4); 
    return 0; 
}

执行结果:

    parameter 1
    parameter 2
    parameter 3
    parameter 4
    empty

(2)非递归方式展开

#include <iostream> 
using namespace std; 

template <class T> 
void print(T arg) 
{ 
    cout << arg << endl; 
} 

template <class ... Args> 
void expand(Args ... args) 
{ 
    int a[] = { (print(args), 0)... }; 
} 
int main() 
{ 
    expand(1, 2, 3, 4); 
    return 0; 
}

执行结果:

    1
    2
    3
    4

【示例】通过可变参数模板实现打印函数

#include <iostream> 
#include <stdexcept> 
using namespace std; 

void Debug(const char* s) 
{ 
    while (*s) 
    { 
        if (*s == '%' && *++s != '%') 
        { 
            throw runtime_error("invalid format string: missing arguments"); 
        } 
        cout << *s++; 
    } 
} 

template<typename T, typename... Args> 
void Debug(const char* s, T value, Args... args) 
{ 
    while (*s) 
    { 
        if (*s == '%' && *++s != '%') 
        { 
            cout << value; return Debug(++s, args...); 
        } 
        cout << *s++; 
    } 
    throw runtime_error("extra arguments provided to Debug"); 
} 
int main() 
{ 
    Debug("a = %d, b = %c, c = %s\n", 250, 'm', "mike"); 
    return 0; 
}

执行结果:

    a = 250, b = m, c = mike

2、可变参数模板类
(1)继承方式展开参数包
    可变参数模板类的展开一般需要定义2 ~ 3个类,包含类声明和特化的模板类:

#include <iostream> 
#include <typeinfo> 

using namespace std; 

template<typename... A> class BMW{}; // 变长模板的声明 

template<typename Head, typename... Tail> // 递归的偏特化定义 
class BMW<Head, Tail...> : public BMW<Tail...> 
{//当实例化对象时,则会引起基类的递归构造 
public: 
    BMW() 
    { 
        printf("type: %s\n", typeid(Head).name()); 
    } 
    Head head; 
}; 

template<> class BMW<>{}; // 边界条件 
int main() 
{ 
    BMW<int, char, float> car; 
    return 0; 
}

执行结果:

    type:f
    type:c
    tepy:i

(2)模板递归和特化方式展开参数包

#include <iostream> 
using namespace std; 

template <long... nums> struct Multiply;// 变长模板的声明 

template <long first, long... last> 
struct Multiply<first, last...> // 变长模板类 
{ 
    static const long val = first * Multiply<last...>::val; 
}; 

template<> struct Multiply<> // 边界条件 
{ 
    static const long val = 1; 
}; 
int main() 
{ 
    cout << Multiply<2, 3, 4, 5>::val << endl; // 120 
    return 0; 
}

执行结果:

    120
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

24、【C++】C++11新特性:Lamda表达式/可变参数模板 的相关文章

  • 将复选框添加到 UniformGrid

    我正在尝试将复选框动态添加到 wpf 中的统一网格中 但看起来网格没有为它们分配足够的空间 所以它们都有点互相重叠 这就是我将它们添加到后面的代码中的方法 foreach string folder in subfolders PathCh
  • 检查两个数是否是彼此的排列?

    给定两个数字 a b 使得 1 例如 123 是 312 的有效排列 我也不想对数字中的数字进行排序 如果您指的是数字的字符 例如 1927 和 9721 则 至少 有几种方法 如果允许排序 一种方法是简单地sprintf将它们放入两个缓冲
  • 无法使用已与其底层 RCW 分离的 COM 对象。在 oledb 中

    我收到此错误 但我不知道我做错了什么 下面的代码在backrgroundworker中 将异常详细信息复制到剪贴板 System Runtime InteropServices InvalidComObjectException 未处理 通
  • C# 和 Javascript SHA256 哈希的代码示例

    我有一个在服务器端运行的 C 算法 它对 Base64 编码的字符串进行哈希处理 byte salt Convert FromBase64String serverSalt Step 1 SHA256Managed sha256 new S
  • Qt-Qlist 检查包含自定义类

    有没有办法覆盖加载自定义类的 Qt QList 的比较机制 即在 java 中你只需要重写一个比较方法 我有一个带有我的自定义类模型的 QList QList
  • 将数组向左或向右旋转一定数量的位置,复杂度为 o(n)

    我想编写一个程序 根据用户的输入 正 gt 负 include
  • 当 contains() 工作正常时,xpath 函数ends-with() 工作时出现问题

    我正在尝试获取具有以特定 id 结尾的属性的标签 like span 我想获取 id 以 国家 地区 结尾的跨度我尝试以下xpath span ends with id Country 但我得到以下异常 需要命名空间管理器或 XsltCon
  • Json.NET - 反序列化接口属性引发错误“类型是接口或抽象类,无法实例化”

    我有一个类 其属性是接口 public class Foo public int Number get set public ISomething Thing get set 尝试反序列化Foo使用 Json NET 的类给我一条错误消息
  • 指针减法混乱

    当我们从另一个指针中减去一个指针时 差值不等于它们相距多少字节 而是等于它们相距多少个整数 如果指向整数 为什么这样 这个想法是你指向内存块 06 07 08 09 10 11 mem 18 24 17 53 7 14 data 如果你有i
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • 在数据库中搜索时忽略空文本框

    此代码能够搜索数据并将其加载到DataGridView基于搜索表单文本框中提供的值 如果我将任何文本框留空 则不会有搜索结果 因为 SQL 查询是用 AND 组合的 如何在搜索 从 SQL 查询或 C 代码 时忽略空文本框 private
  • 从路径中获取文件夹名称

    我有一些路c server folderName1 another name something another folder 我如何从那里提取最后一个文件夹名称 我尝试了几件事 但没有成功 我只是不想寻找最后的 然后就去休息了 Thank
  • for循环中计数器变量的范围是多少?

    我在 Visual Studio 2008 中收到以下错误 Error 1 A local variable named i cannot be declared in this scope because it would give a
  • C++ 复制初始化和直接初始化,奇怪的情况

    在继续阅读本文之前 请阅读在 C 中 复制初始化和直接初始化之间有区别吗 https stackoverflow com questions 1051379 is there a difference in c between copy i
  • 控制到达非 void 函数末尾 -wreturn-type

    这是查找四个数字中的最大值的代码 include
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • 将文本叠加在图像背景上并转换为 PDF

    使用 NET 我想以编程方式创建一个 PDF 它仅包含一个背景图像 其上有两个具有不同字体和位置的标签 我已阅读过有关现有 PDF 库的信息 但不知道 如果适用 哪一个对于如此简单的任务来说最简单 有人愿意指导我吗 P D 我不想使用生成的
  • 在 Dynamics CRM 插件中访问电子邮件发件人地址

    我正在编写一个 Dynamics CRM 2011 插件 该插件挂钩到电子邮件实体的更新后事件 阶段 40 pipeline http msdn microsoft com en us library gg327941 aspx 并且在此阶
  • C - 直接从键盘缓冲区读取

    这是C语言中的一个问题 如何直接读取键盘缓冲区中的数据 我想直接访问数据并将其存储在变量中 变量应该是什么数据类型 我需要它用于我们研究所目前正在开发的操作系统 它被称为 ICS OS 我不太清楚具体细节 它在 x86 32 位机器上运行
  • const、span 和迭代器的问题

    我尝试编写一个按索引迭代容器的迭代器 AIt and a const It两者都允许更改容器的内容 AConst it and a const Const it两者都禁止更改容器的内容 之后 我尝试写一个span

随机推荐

  • web3钱包系统开发

    web3技术概念介绍 近期 演员周星驰在ins开通首个社交账号 并发布人才招募令 在漆黑中找寻鲜明出众的Web3人才 将 Web3 带入大众视野 但有不少人对其感到陌生 到底何为Web3 早在2018年 就有人开始谈论web3了 它其实是一
  • CSS中关于z-index的堆叠顺序

    1 同级的z index div class container div class div1 h1 Division Element 1 z index 10 h1 div div class div2 h1 Division Eleme
  • Vs2019简单快速的打包可安装项目(图文教程)

    声明本项目在已安装vs2019和加载了installer Projects的情况下才能操作 右键解决方案 gt 添加 gt 新建项目 新建一个Setup Project 进入这个页面 右键Application Foluder gt Add
  • CVPR 2023

    作者 张倩 小舟 来源 机器之心 在文生图领域 扩散模型似乎已经一统天下 让曾经也风头无两的 GAN 显得有些过时 但两相比较 GAN 依然存在不可磨灭的优势 这使得一些研究者在这一方向上持续努力 并取得了非常实用的成果 相关论文已被 CV
  • 遍历Github仓库并提取所有图片

    遍历Github仓库并提取所有图片 项目介绍 一个简易的Github图床客户端 项目仓库 GithubImageHost 利用 QElapsedTimer QCoreApplication processEvents 可是实现UI同步 QE
  • html静态页面中引入scss的样式调整

    问题 静态页面样式和vue动态页面的样式不统一 截图了一个角落 需求 静态页面的样式要和动态页面的一样 解决步骤 F12 查看样式文件引用的区别 找到vue动态页面中的样式区别是在vue项目中用了自带的scss文件 将文件引入到静态页面的文
  • SSH通道的Kettle链接MySQL方法

    参考文献 http www ukettle org thread 452 1 1 html 对于采用SSH通道的MySQL服务器 Kettle无法直接连接 需要使用到 使用 SSH 工具 PUTTY
  • yolov5的TensorRT部署--warpaffine_cuda核函数

    从0到1实现基于tensorrt的yolo部署教程 http t csdn cn HUn4T 请点击该链接 即可看到全文 本文对于上面的案例 将预处理使用cuda核函数进行加速 一 cuda核函数的基本概念 1 1 CUDA C基础 核函数
  • Redis入门(一)

    1 简介 Redis 是完全开源的 遵守 BSD 协议 是一个高性能的 key value 数据库 Redis 与其他 key value 缓存产品有以下三个特点 Redis支持数据的持久化 可以将内存中的数据保存在磁盘中 重启的时候可以再
  • 计算机组成原理实验二 存储系统预习报告

    实验一 静态RAM 一 实验目的 掌握静态随机存储器 RAM 工作特性及数据的读写方法 基于信号时序图 了解读写静态随机存储器的原理 二 实验预习 1 阅读实验指导书 然后回答问题 实验所用的静态存储器由一片 6116 2K 8bit 构成
  • 离散信号的Matlab表示

    对任意离散序列x k 需用2个向量来表示 一个表示k的取值范围 另一个表示序列的值 例如序列x k 2 1 1 1 3 0 2 可用Matlab表示为 k 2 4 x 2 1 1 1 3 0 2 若序列从0开始 则只用一个向量x就可表示序列
  • 前端开发: 微信小程序 (文字,链接)生成二维码

    首先最主要的还是通过weapp qrcode js 靠这个轮子就可以了 GitHub yingye weapp qrcode weapp qrcode js 在 微信小程序 中 快速生成二维码https github com yingye
  • 人工智能和机器学习

    机器学习 1 什么是机器学习 在进行特定的编程的情况下 给与计算机学习能力的领域 机器学习是从数据中自动分析获得模型 并利用模型对未知数据进行预测 2 机器学习与人工智能 2 1人工智能发展的三个阶段 1980年代是正式形成时期 1990
  • Java接口详解

    http hi baidu com cxgfhfiupuanour item 370967f74ecbe9cca835a2b4 对初学者来说 接口不是很好理解 现将某高手的一篇文章贴出来 共大家分享 我们来看一个类 class A priv
  • 华为UOS欧拉版 K3S+Rancher 安装完全版

    文章目录 K3S服务 happy path安装过程 1 准备工作 1 1 修改网卡名称为eth0 1 2 切换yum源 1 3 关闭防火墙以及selinux 1 4 修改主机名 并修改hosts 1 5 在UOS 基于华为欧拉 上安装doc
  • vue动画之轮播图

  • Vue自定义组件——封装一个简单的可拖拽的弹出框 可拖拽的Dialog

    首先明确需要传入组件的属性 Props dialogVisible Number 非0打开 allowDrag Boolean 是否可以拖拽 noFoot Boolean 是否显示按钮行 submit Function 点击提交按钮的回调
  • Tomcat 环境变量

    到tomcat官方站点 http www apache org dist jakarta tomcat 4 下载tomcat jakarta tomcat 4 1 30 exe 下载之后安装 比如安装在D Tomcat下 安装完之后 设置环
  • AsyncResult 类的使用

    AsyncResult 类封装异步委托上的异步操作的结果 与异步委托一起使用 从该委托的 BeginInvoke 方法返回的 IAsyncResult 可以强制转换为 AsyncResult AsyncResult 具有 AsyncDele
  • 24、【C++】C++11新特性:Lamda表达式/可变参数模板

    一 Lamda表达式 Lamda表达式是C 11中引入的一项新技术 利用Lamda表达式可以编写内嵌的匿名函数 用以替换独立函数或者函数对象 并且使得代码更可读 是一种匿名函数 即没有函数名的函数 Lamda函数的语法定义如下 captur