当前位置: 首页 > news >正文

Modern Effective C++条款三十四:考虑lambda而非std::bind

C++11中的std::bind是C++98的std::bind1ststd::bind2nd的后续,C++11 lambda几乎总是比std::bind更好的选择。 从C++14开始,lambda的作用不仅强大,而且是完全值得使用的。与item32中一样,我们将从std::bind返回的函数对象称为bind对象(bind objects)。优先lambda而不是std::bind的最重要原因是lambda更易读。

Lambda 表达式版本

#include <chrono>
#include <functional>
// 假设 setAlarm 已定义
void setAlarm(std::chrono::steady_clock::time_point t,enum class Sound s,
std::chrono::seconds d);
enum class Sound {Beep,Siren,Whistle};
//使用lambda创建一个函数对象,设置一小时后响30秒的警报器
auto setSoundL = [](Sound s) {using namespace std::chrono;using namespace std::literals; // 对于 C++14 后缀setAlarm(steady_clock::now() + 1h, s, 30s);
};

创建一个函数对象(即lambda),接受一个Sound类型的参数,并用这个声音参数调用setAlarm,同时自动计算一小时后的时间点以及30秒的持续时间。使用C++14的时间字面量(如1h和30s)让代码更简洁。延迟求值:在lambda内部,steady_clock::now() + 1h是在调用lambda时才被计算,这意味着警报会在实际调用时的一小时后触发。

std::bind 版本 (原始尝试)

#include <chrono>
#include <functional>
using namespace std::chrono;
using namespace std::placeholders;
//错误的 std::bind 调用
auto setSoundB = std::bind(setAlarm, steady_clock::now() + 1h, _1, 30s); 
//不正确!std::bind 版本 (修正后的)
//修正后的 std::bind 调用,使用嵌套 bind 推迟计算
auto setSoundB = std::bind(setAlarm,std::bind(std::plus<>(), std::bind(steady_clock::now), 1h),_1,30s
);

std::bind调用不正确的原因在于steady_clock::now() + 1h的求值时机。当使用steady_clock::now() + 1h作为std::bind的一个参数时,这个表达式是在std::bind被调用的那一刻就被计算(即立即求值),而不是在最终调用setAlarm时计算。

第一层 std::bind

setAlarm是最终要调用的目标函数。时间点:第二层std::bind的结果。声音类型:_1占位符,最终调用setSoundB时,传递一个Sound类型的值给它,这个值会被当作setAlarm的第二个参数。持续时间:30s,是一个固定的持续时间,代表警报响铃的时长。

第二层 std::bind

std::bind(std::plus<>(), std::bind(&std::chrono::steady_clock::now), 1h)

目标操作:std::plus<>():加法操作。(使用当前时间(左侧操作数)加上1h(右侧操作数))

左侧操作数:通过第三层std::bind获取,即当前时间steady_clock::now()。右侧操作数:1h,表示一小时的时间段。

第三层 std::bind

std::bind(&std::chrono::steady_clock::now)

&std::chrono::steady_clock::now取的是steady_clock::now函数的地址,为了推迟求值。当外部的setSoundB被调用时,这层std::bind会调用steady_clock::now()来获取当前时间。创建一个函数对象,该对象封装了对 std::chrono::steady_clock::now() 函数的调用。&std::chrono::steady_clock::now:取 steady_clock::now 函数的地址。steady_clock::now 是一个静态成员函数,它返回一个代表当前时间点的对象。通过在前面加上&,获取函数的指针。

std::bind:模板函数,用于创建一个函数对象,该对象可以存储、复制和调用一个可调用对象(如函数、lambda表达式或其它函数对象)以及与之关联的一组参数。

当执行 std::bind(&std::chrono::steady_clock::now)时,实际上在创建一个函数对象,不带任何参数,并且当这个函数对象被调用时,会调用 steady_clock::now() 来获取当前的时间点。

std::bind 版本 (C++11)

#include <chrono>
#include <functional>
using namespace std::chrono;
using namespace std::placeholders;
// C++11 中等价于 lambda 的 std::bind 实现
auto setSoundB = std::bind(setAlarm,std::bind(std::plus<steady_clock::time_point>(), std::bind(&std::chrono::steady_clock::now), hours(1)),_1,seconds(30)
);

Lambda 表达式更易读、更直观,且能自然地处理延迟求值。std::bind虽然功能强大,但语法复杂,特别是在需要推迟计算时,会导致代码难以理解和维护。

创建一个检查值是否在一个范围内的函数对象

// C++11版本使用std::bind
auto betweenB = std::bind(std::logical_and<bool>(),std::bind(std::less_equal<int>(),lowVal,_1),std::bind(std::less_equal<int>(),_1,highVal));
// C++11版本使用Lambda表达式
auto betweenL = [lowVal,highVal](int val)
{ return lowVal<=val&&val<=highVal; };

在C++11中,std::bind可用于模拟移动捕获和创建多态函数对象。C++14随着lambda对初始化捕获的支持以及auto形参的引入,这些特殊情况也不再是std::bind的优势所在。

lambda相比,使用std::bind进行编码的代码可读性较低,表达能力较低,并且效率可能较低。 在C++14中,没有std::bind的合理用例。 但是,在C++11中,可以在两个受约束的情况下证明使用std::bind是合理的:

  • 移动捕获。C++11的lambda不提供移动捕获,但是可以通过结合lambdastd::bind来模拟。 有关详细信息,请参阅item32,该条款还解释了在C++14中,lambda对初始化捕获的支持消除了这个模拟的需求。
  • 多态函数对象。因为bind对象上的函数调用运算符使用完美转发,所以它可以接受任何类型的实参(以item30中描述的完美转发的限制为界限)。当要绑定带有模板化函数调用运算符的对象时,此功能很有用。
class PolyWidget {
public:template<typename T>void operator()(const T& param);
};

std::bind可以如下绑定一个PolyWidget对象:

PolyWidget pw;
auto boundPW = std::bind(pw, _1);

boundPW可以接受任意类型的对象了:

boundPW(1930);              //传int给PolyWidget::operator()
boundPW(nullptr);           //传nullptr给PolyWidget::operator()
boundPW("Rosebud"); 		//传字面值给PolyWidget::operator()

这一点无法使用C++11的lambda做到。 但是,在C++14中,可以通过带有auto形参的lambda轻松实现:

auto boundPW = [pw](constauto& param)  //C++14 { pw(param); };

在C++11中增加了lambda支持,这使得std::bind几乎已经过时了,从C++14开始,更是没有很好的用例了。

请记住:

  • 与使用std::bind相比,lambda更易读,更具表达力并且可能更高效。
  • 只有在C++11中,std::bind可能对实现移动捕获或绑定带有模板化函数调用运算符的对象时会很有用。

相关文章:

Modern Effective C++条款三十四:考虑lambda而非std::bind

C11中的std::bind是C98的std::bind1st和std::bind2nd的后续&#xff0c;C11 lambda几乎总是比std::bind更好的选择。 从C14开始&#xff0c;lambda的作用不仅强大&#xff0c;而且是完全值得使用的。与item32中一样&#xff0c;我们将从std::bind返回的函数对象称为bind对象(bi…...

lyapunov指数的绘制

有如下方程&#xff1a; %% 方程式 % x(n1)1y(n)-a*x(n)^2 % y(n1)b*x(n)绘制其对应的lyapunov指数。 MATLAB实现方式&#xff1a; clc; clearvars; close all;%% 方程式 % x(n1)1y(n)-a*x(n)^2 % y(n1)b*x(n)%% 代码 N 1000; a (0:0.001:1.4); b 0.3; na length(a…...

Ansible 运维工具

安装 apt install ansible /etc/ansible/hosts , 指定密码或密钥访问分组机器 [k8s_masters] master0.c0.k8s.sb[k8s_nodes] node0.c0.k8s.sb node1.c0.k8s.sb[k8s:children] k8s_masters k8s_nodes[k8s_masters:vars] ansible_ssh_usersbadmin ansible_ssh_pass"***&q…...

【AI系统】MobileNet 系列

MobileNet 系列 在本文会介绍 MobileNet 系列&#xff0c;重点在于其模型结构的轻量化设计&#xff0c;主要介绍详细的轻量化设计原则&#xff0c;基于这原则&#xff0c;MobileNetV1 是如何设计成一个小型&#xff0c;低延迟&#xff0c;低功耗的参数化模型&#xff0c;可以满…...

MATLAB在生态环境数据处理与分析中的应用

专题一 MATLAB编程入门 要点&#xff1a;介绍、案例演示、软件界面、语法基础、基本运算等 专题二&#xff08;试听&#xff09; MATLAB编程入门 要点&#xff1a;脚本编写、函数调用、循环控制、代码调试、文件读写等 专题三 MATLAB可视化与绘图 要点&#xff1a;交互式…...

tensorrt

engine /*tensorrt创建builder1. 创建builder2. 创建网络定义&#xff1a;builder-->network3. 配置参数&#xff1a;builder-->config4. 生成engine&#xff1a;builder-->engine()5. 序列化保存:engine-->serialize6. 释放资源&#xff1a;delete */ #include&l…...

利用Grounding DINO进行自动标注——目标检测任务——YOLO格式

关于Grounding DINO的环境搭建可以参考我的以前的博客&#xff0c;链接如下所示 如何在Linux上离线部署Grounding DINO-CSDN博客 这个博客主要来介绍如何利用Grounding DINO这个项目去进行目标检测的自动化标注。并且给出了相关的代码已经实验验证。 1.数据集准备 2. 开始实…...

网际协议(IP)与其三大配套协议(ARP、ICMP、IGMP)

网际协议&#xff08;Internet Protocol&#xff0c;IP&#xff09;&#xff0c;又称互联网协议。是OSI中的网络层通信协议&#xff0c;用于跨网络边界分组交换。它的路由功能实现了互联互通&#xff0c;并从本质上建立了互联网。网际协议IP是 TCP/IP 体系中两个最主要的协议之…...

uniapp 添加loading

在uniapp中添加loading可以使用uni的API uni.showLoading 方法。以下是一个简单的示例代码 // 显示loading uni.showLoading({title: 加载中 });// 假设这里是异步操作&#xff0c;比如网络请求 setTimeout(function () {// 隐藏loadinguni.hideLoading(); }, 2000);...

cocotb pytest

打印python中的print &#xff0c; 应该使用 pytest -s pytest --junitxmltest_report.xml --htmlreport.html...

docker run 设置启动命令

在使用 docker run 命令时&#xff0c;你可以通过指定启动命令来覆盖 Docker 镜像中的默认入口点或命令。具体来说&#xff0c;你可以通过以下两种方式来设置启动命令&#xff1a; 覆盖 CMD&#xff1a; 你可以通过在 docker run 命令的最后部分提供命令来覆盖镜像的默认 CMD 指…...

docker入门 自记录

1.先自己下载离线bao .tar 或者 自己pull docker pull xxx 如果遇到网络问题就换源 2.之后run一个docker 后面是映射本地路径 sudo docker run -it --name ultralytics_241124 --gpus all --shm-size 8G -v /home/oppenheim/detect/train241204/docker:/home/docker ultralyti…...

css实现圆周运动效果

在CSS中可以通过 keyframes 动画 和 transform 属性实现元素的圆周运动。以下是一个示例代码&#xff1a; 示例代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content…...

【NoSQL数据库】MongoDB数据库——集合和文档的基本操作(创建、删除、更新、查询)

目录 一、MongoDB数据库原理 二、MongoDB数据库和集合基本操作&#xff08;增删改查&#xff09; 三、MongoDB数据库的文档基本操作&#xff08;增删改&#xff09; 四、学习笔记 往期文章&#xff1a;【NoSQL数据库】MongoDB数据库的安装与卸载-CSDN博客 一、MongoDB数据…...

Dart 学习笔记(一)

一、数据类型 数值类型 int&#xff1a;表示整数类型&#xff0c;例如&#xff1a;int num 10;。在 Dart 中&#xff0c;int 类型是有范围限制的&#xff0c;具体取决于平台&#xff0c;但通常在 -2^63 到 2^63 - 1 之间。double&#xff1a;表示双精度浮点数类型&#xff0c…...

安防视频监控平台Liveweb视频汇聚管理系统管理方案

智慧安防监控Liveweb视频管理平台能在复杂的网络环境中&#xff0c;将前端设备统一集中接入与汇聚管理。国标GB28181协议视频监控/视频汇聚Liveweb平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、…...

十八(GIT)、GIT基本命令、axios别名方法、黑马就业数据平台(axios基地址、轻提示函数、注册及登录功能)

1. GIT 1.1 Git配置用户信息 1. Git配置用户信息 git config --global user.name "用户名" git config --global user.email 邮箱地址 2. 查看配置 git config --list (信息太多使用 输入 q 退出) 1.2 本地初始化Git仓库 1. 通常有两种获取Git仓库的方式: 将 尚未进…...

Linux查看系统基本信息

执行命令 chmod x system_info.sh && ./system_info.sh文件名&#xff1a;system_info.sh #!/bin/bash# 获取系统版本 SYSTEM_VERSION$(cat /etc/os-release | grep ^PRETTY_NAME | cut -d -f 2 | xargs)# 获取CPU数量 CPU_COUNT$(grep -c ^processor /proc/cpuinfo…...

Word处理表格的一些宏

目录 1、表格首行居中2、表格内容靠左上下居中&#xff08;排除首行&#xff09; 1、表格首行居中 说明&#xff1a; 遇到错误将进行捕获&#xff0c;然后继续处理下一个表格 宏&#xff1a; Sub 表格首行居中()Dim tbl As tableOn Error Resume Next 错误时继续执行下一个…...

将本地项目文件推送到Git仓库中

一、在GitLab中创建新项目 1. 创建新项目 2. 打开本地文件. 3. 项目git文件操作 初始化本地Git仓库 git init关联远程仓库 git remote add origin 远程仓库地址将文件到Git暂存区 git add .提交项目文件到Git本地仓库 git commit -m "提交内容说明"将本地…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

负载均衡器》》LVS、Nginx、HAproxy 区别

虚拟主机 先4&#xff0c;后7...

基于 HTTP 的单向流式通信协议SSE详解

SSE&#xff08;Server-Sent Events&#xff09;详解 &#x1f9e0; 什么是 SSE&#xff1f; SSE&#xff08;Server-Sent Events&#xff09; 是 HTML5 标准中定义的一种通信机制&#xff0c;它允许服务器主动将事件推送给客户端&#xff08;浏览器&#xff09;。与传统的 H…...

理想汽车5月交付40856辆,同比增长16.7%

6月1日&#xff0c;理想汽车官方宣布&#xff0c;5月交付新车40856辆&#xff0c;同比增长16.7%。截至2025年5月31日&#xff0c;理想汽车历史累计交付量为1301531辆。 官方表示&#xff0c;理想L系列智能焕新版在5月正式发布&#xff0c;全系产品力有显著的提升&#xff0c;每…...

Ubuntu 可执行程序自启动方法

使用 autostart&#xff08;适用于桌面环境&#xff09; 适用于 GNOME/KDE 桌面环境&#xff08;如 Ubuntu 图形界面&#xff09; 1. 创建 .desktop 文件 sudo vi ~/.config/autostart/my_laser.desktop[Desktop Entry] TypeApplication NameMy Laser Program Execbash -c &…...