c++匿名比较函数参数顺序逻辑

news2024/11/25 2:23:00


在使用lower_bound和upper_bound时,想自定义比较函数,在这个过程中出现了参数定义顺序导致的错误问题,于是查找学习了下自定义比较函数需要符合的规则。

目录

1 lower_bound和upper_bound函数

1.1 lower_bound

1.2 upper_bound

2 问题产生

3 问题研究及原因

4 总结


1 lower_bound和upper_bound函数

1.1 lower_bound

lower_bound 函数返回一个迭代器,指向容器中第一个大于或等于给定值的元素。如果容器中没有这样的元素,则返回容器的 end() 迭代器。

语法:

iterator lower_bound(iterator begin, iterator end, const value_type& value);
iterator lower_bound(iterator begin, iterator end, const value_type& value, Compare compare);

参数:

  • beginend:指定要搜索的容器的范围。
  • value:要查找的值。
  • compare:比较函数。

返回值:

一个迭代器,指向容器中第一个大于或等于 value 的元素。如果容器中没有这样的元素,则返回 end() 迭代器。

示例:

std::vector<int> v = {1, 4, 5, 7, 9};

// 查找第一个大于或等于 4 的元素
auto it = std::lower_bound(v.begin(), v.end(), 4);

// 输出迭代器指向的元素
std::cout << *it << std::endl; // 输出 4

1.2 upper_bound

upper_bound 函数返回一个迭代器,指向容器中第一个大于给定值的元素。如果容器中没有这样的元素,则返回容器的 end() 迭代器。

语法:

iterator upper_bound(iterator begin, iterator end, const value_type& value);
iterator upper_bound(iterator begin, iterator end, const value_type& value,Compare compare);

参数:

  • beginend:指定要搜索的容器的范围。
  • value:要查找的值。

返回值:

一个迭代器,指向容器中第一个大于 value 的元素。如果容器中没有这样的元素,则返回 end() 迭代器。

示例:

std::vector<int> v = {1, 4, 5, 7, 9};

// 查找第一个大于 4 的元素
auto it = std::upper_bound(v.begin(), v.end(), 4);

// 输出迭代器指向的元素
std::cout << *it << std::endl; // 输出 5

从上面示例中可以看到二者的区别:

  • lower_bound 查找第一个大于或等于给定值的元素,
  • upper_bound 查找第一个大于给定值的元素。

起初,我认为二者的区别仅仅在此,但很快,就遇到了新的问题。

2 问题产生

观察这两行代码:

auto left = lower_bound(intervals.begin(),intervals.end(),newInterval[0],[](auto& a, int b) { return a[1] < b; });
auto right = upper_bound(left,intervals.end(),newInterval[1],[](int b,auto& a) { return a[0] > b; });

或许乍一看没什么特别的,但是强迫症一定能看到问题所在:两个匿名函数定义的参数顺序太不整齐了。

但当我尝试交换参数顺序使其看起来整齐一些,啪的一下,编译出错。

In file included from prog_joined.cpp:1:
In file included from /leetcode/precompiled/headers.h:13:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/cmath:1935:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/specfun.h:45:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_algobase.h:71:
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/predefined_ops.h:240:16: error: no matching function for call to object of type '(lambda at prog_joined.cpp:14:70)'
  231 |         { return bool(_M_comp(__val, *__it)); }
      |                       ^~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_algo.h:2041:8: note: in instantiation of function template specialization '__gnu_cxx::__ops::_Val_comp_iter<(lambda at prog_joined.cpp:14:70)>::operator()<const int, __gnu_cxx::__normal_iterator<std::vector<int> *, std::vector<std::vector<int>>>>' requested here
 2032 |           if (__comp(__val, __middle))
      |               ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_algo.h:2108:19: note: in instantiation of function template specialization 'std::__upper_bound<__gnu_cxx::__normal_iterator<std::vector<int> *, std::vector<std::vector<int>>>, int, __gnu_cxx::__ops::_Val_comp_iter<(lambda at prog_joined.cpp:14:70)>>' requested here
 2099 |       return std::__upper_bound(__first, __last, __val,
      |                   ^
Line 5: Char 22: note: in instantiation of function template specialization 'std::upper_bound<__gnu_cxx::__normal_iterator<std::vector<int> *, std::vector<std::vector<int>>>, int, (lambda at solution.cpp:14:70)>' requested here
    5 |         auto right = upper_bound(left,intervals.end(),newInterval[1],[](auto& a, int b) { return a[0] > b; });
      |                      ^
Line 5: Char 70: note: candidate function template not viable: no known conversion from 'std::vector<int>' to 'int' for 2nd argument
    5 |         auto right = upper_bound(left,intervals.end(),newInterval[1],[](auto& a, int b) { return a[0] > b; });
      |                                                                      ^           ~~~~~
1 error generated.

3 问题研究及原因

难道我们定义的匿名函数都不可以随便定义吗?

并非如此,将匿名函数拉出upper_bound和lower_bound放到其他位置,运行的好好的。

那基本可以确定问题处在upper_bound和lower_bound上。

这里要理解像这样一条语句,究竟是如何运行的?编译器怎么知道我们的a和b究竟是什么东西?

auto left = lower_bound(intervals.begin(),intervals.end(),newInterval[0],[](auto& a, int b) { return a[1] < b; });

那么就涉及到了lower_bound的具体实现

可以想到的是,其内部将intervals这个容器的起始到结尾作为需要比较的对象,newInterval[0]这个值作为目标值。

那它又如何确定a对应的intervals容器还是newInterval[0]这个值?

答案是约定,它需要约定我们传入参数的顺序,也就是出现问题的这个匿名函数参数顺序的地方。

来看lower_bound的源码:

template<class ForwardIt, class T, class Compare>
ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value, Compare comp)
{
    ForwardIt it;
    typename std::iterator_traits<ForwardIt>::difference_type count, step;
    count = std::distance(first, last);
 
    while (count > 0)
    {
        it = first;
        step = count / 2;
        std::advance(it, step);
 
        if (comp(*it, value))
        {
            first = ++it;
            count -= step + 1;
        }
        else
            count = step;
    }
 
    return first;
}

注意这一句:

comp(*it, value)

它约定了比较函数的第一个参数是容器内容,第二个参数是目标值。

那upper_bound呢?

template<class ForwardIt, class T, class Compare>
ForwardIt upper_bound(ForwardIt first, ForwardIt last, const T& value, Compare comp)
{
    ForwardIt it;
    typename std::iterator_traits<ForwardIt>::difference_type count, step;
    count = std::distance(first, last);
 
    while (count > 0)
    {
        it = first; 
        step = count / 2;
        std::advance(it, step);
 
        if (!comp(value, *it))
        {
            first = ++it;
            count -= step + 1;
        } 
        else
            count = step;
    }
 
    return first;
}

好嘛,正好是反过来的。

comp(value, *it)

4 总结

  1. 在自定义比较函数的时候需要知道函数原型,即约定的哪个参数代表什么含义,这样编译器才会知道我们传入的是什么。
  2. 对于upper_bound,比较函数的第一个参数是目标值,第二个参数是需要比较的容器内容。
  3. 对于lower_bound,比较函数的第一个参数是需要比较的容器内容,第二个参数是目标值。
  4. lower_bound和upper_bound的开发维护人员,一定不是强迫症。
  5. 附一张文心一言画得强迫症,好像emmm......一言难尽

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

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

相关文章

服务器关机前未退出xampp导出MySQL无法启动

背景解决 五一放假&#xff0c;服务器关机了&#xff0c;但是关机前没有正常关闭数据库服务&#xff0c;导致数据库无法启动&#xff01; 查看错误日志如下 从报错信息可以看出是MySQL这个服务相关文件出现问题了&#xff0c;解决思路&#xff1a;重新安装xampp 重新安装xam…

OpenHarmony实战开发-管理位置权限

Web组件提供位置权限管理能力。开发者可以通过onGeolocationShow()接口对某个网站进行位置权限管理。Web组件根据接口响应结果&#xff0c;决定是否赋予前端页面权限。获取设备位置&#xff0c;需要开发者配置ohos.permission.LOCATION&#xff0c;ohos.permission.APPROXIMATE…

react引入阿里矢量库图标

react引入阿里矢量库图标 登录阿里矢量库&#xff0c;将项目所需的图标放一起 react项目中新建文件夹MyIcon.js 3. 在页面中引入&#xff0c;其中type为图标名称

JSON++介绍

1.简介 JSON 是一个轻量级的 JSON 解析库&#xff0c;它是 JSON&#xff08;JavaScript Object Notation&#xff09;的一个超集。整个代码由一个单独的头文件json.hpp组成&#xff0c;没有库&#xff0c;没有子项目&#xff0c;没有依赖项&#xff0c;没有复杂的构建系统&…

【RabbitMQ 三】Java客户端开发

本文引用的代码源自《RabbitMQ实战指南》 关键的类和接口主要有Channel、Connection、ConnectionFactory、Consumer等&#xff0c;它们主要的作用如下&#xff1a; Channel&#xff1a;实现AMQP协议层的操作Connection&#xff1a;开启信道&#xff08;Channel&#xff09;、注…

黑马点评项目总结

登录 基于session登录 短信验证码登录 配置登录拦截器 向 Spring MVC 框架中添加拦截器&#xff0c;LoginInterceptor 是一个自定义的拦截器&#xff0c;用于拦截用户的登录请求。 excludePathPatterns这一句是设置拦截器需要放行的请求路径列表。 "/user/code", …

ROS机械臂中Movelt!

Movelt!简介 一个易于集成使用的集成化开发平台 由一系列移动操作的功能包组成 1、运动规划 2、操作控制 3、3D感知 4、运动学 5、控制与导航算法 ....... 提供友好的GUI 可应用于工业、商业、研发和其他领域 ROS社区中使用度排名前三的功能包 Movelt!三大核心功能 …

Kafka应用Demo:按主题订阅消费消息

安装环境 Kafka安装可参考官方网站的指导(https://kafka.apache.org/quickstart), 按步骤解压压缩包&#xff0c;修改配置。然后再启动zookeeper和kafka-server即可。 需要注意的一点&#xff1a;如果是在VMware虚拟机上启动的kafka, 需要修改一下server.properties配置文件&am…

JavaEE技术之MySql高级-搭建主从复制(主从同步原理、一主多从配置)

文章目录 MySQL主从同步1、MySQL主从同步原理2、一主多从配置2.1、准备主服务器2.2、准备从服务器2.3、启动主从同步2.4、实现主从同步2.5、停止和重置2.6、常见问题问题1问题2 MySQL主从同步 1、MySQL主从同步原理 基本原理&#xff1a; slave会从master读取binlog来进行数据…

python中如何遍历字典

1. 遍历字典的键key ① >>> d{list:[1, 2, 3],1:123,111:python3,tuple:(4, 5, 6)} >>> for key in d:print(str(key):str(d[key])) list:[1, 2, 3] 1:123 111:python3 tuple:(4, 5, 6) ② >>> d{list:[1, 2, 3],1:123,111:python3,tuple:(4, 5, 6…

书生·浦语大模型实战营之手把手带你评测 Llama 3 能力(OpenCompass 版)

书生浦语大模型实战营之手把手带你评测 Llama 3 能力&#xff08;OpenCompass 版&#xff09; 环境配置 conda create -n llama3 python3.10 pytorch torchvision pytorch-cuda -c nvidia -c pytorch -y conda activate llama3conda install git git-lfs install✨下载 Llama3…

HP Z620 服务器打开VTx虚拟技术

在使用Virtual Box的时候&#xff0c;虚拟主机启动报错&#xff1a;提示需要VTx。于是到bios里面去设置VTx。 这里有个小坑&#xff0c;就是HP 的bios配置里面&#xff0c;VTx不在常规的“System Configuration”、“Advanced”等地方&#xff0c;而是在“Security”菜单里&…

nvcc: command not found

nvcc: command not found nvcc命令是 NVIDIA CUDA 编译器&#xff0c;就类似于gcc是c语言的编译器&#xff0c;用于编译 CUDA 代码并生成 GPU 可执行文件。由于程序是要经过编译器编程成可执行的二进制文件&#xff0c;而cuda程序有两种代码&#xff0c;一种是运行在CPU上的ho…

营销5.0时代,企业的痛如何解?

进入营销5.0阶段之后&#xff0c;许多企业都需解决连接客户效能低下的问题。针对这个问题&#xff0c;产品经理、软件开发公司包括个人开发者&#xff0c;要怎么找到有效的“解药”&#xff1f; 营销不仅每年都在变化&#xff0c;甚至每天都在变化。 ——现代营销学之父&…

js实现json数据可编辑

背景 项目中有低代码平台&#xff0c;由于历史脏数据和非同步编辑的问题&#xff0c;偶尔会出现数据错乱的问题&#xff0c;希望有一个快捷的方式修改数据 之前在用Formily的时候有注意到designable/react 里面的json数据编辑功能非常不错如果能应用到项目里就完美了 design…

《架构风清扬-Java面试系列第29讲》聊聊DelayQueue的使用场景

DelayQueue是BlockingQueue接口的一个实现类之一 这个属于基础性问题&#xff0c;老规矩&#xff0c;我们将从使用场景和代码示例来进行讲解 来&#xff0c;思考片刻&#xff0c;给出你的答案 1&#xff0c;使用场景 实现&#xff1a;延迟队列&#xff0c;其中元素只有在其预定…

图片编辑工具-Gimp

一、前言 GIMP&#xff08;GNU Image Manipulation Program&#xff09;是一款免费开源的图像编辑软件&#xff0c;具有功能强大和跨平台的特性。 GIMP作为一个图像编辑器&#xff0c;它提供了广泛的图像处理功能&#xff0c;包括但不限于照片修饰、图像合成以及创建艺术作品…

Day28:ElasticSearch入门、Spring整合ES、开发社区搜索功能

ElasticSearch入门 Elasticsearch简介 一个分布式的、Restful风格的搜索引擎。支持对各种类型的数据的检索&#xff08;非结构化的也可以&#xff09;。搜索速度快&#xff0c;可以提供实时的搜索服务。便于水平扩展&#xff08;集群式部署&#xff09;&#xff0c;每秒可以处…