为什么 max() 应该写成 b < a ? a : b 呢?
在 《 C++ Templates 2nd Edition 》Chapter 1 中,作者将 max() 模板定义如下:
template <typename T> T max(T a, T b)
{
return b < a ? a : b; // 而不是 return a < b ? b : a;
}
同时在书下注释里,作者写道 :“Note that the max() template according to [StepanovNotes] intentionally returns “b < a ? a : b” instead of “a < b ? b : a” to ensure that the function behaves correctly even if the two values are equivalent but not equal. ”
翻译过来就是 : 根据 [StepanovNotes] 故意写成这样的, 为的就是,当两个值 ' equivalent ' 但不 ' equal ' 的时候函数也能表现正常。
但这个 equivalent 但不 equal ,让人很难理解。
cppreference 上说,For the types that are both EqualityComparable and LessThanComparable, the C++ standard library makes a distinction between equality, which is the value of the expression a == b and equivalence, which is the value of the expression !(a < b) && !(b < a).
意思就是:
- 满足 !(a < b) && !(b < a) 的时候, a 和 b 是 equivalent 的, 因为 a 不小于 b 而且 b 不小于 a。两个变量是否是 equivalence ,由 operator< 决定。
- 满足 a == b 的时候, a 和 b 是 equal 的, 因为 a 等于 b。 两个变量是否是 equal,由 operator== 决定。
回到开头,为何要写成 return b < a ? a : b; 呢?
看下面这个例子:
int x = , y = ;
const int& max = std::max(x, y);
const int& min = std::min(x, y);
std::cout
<< "std::min(x, y)" << std::endl
<< "x :\t" << &x << std::endl
<< "y :\t" << &y << std::endl
<< "max :\t" << &max << std::endl
<< "min :\t" << &min << std::endl;

对 std::max(x, y) 和 std::min(x, y) 的结果进行引用时,返回的都是 x。
这只是基本数据类型,只要 !(a < b) && !(b < a) 就可以认为是 a == b,虽然他们的地址不同。这和在Python里 a == b 和 a is b 的区别异曲同工。但若是对自定义数据类型这么搞,就可能完蛋,因为operator< 和 operator== 的判断标准不一定相同。
比如 :
struct 学生
{
int 成绩;
string 名字;
string 学号;
bool operator<(学生 乙)
{
return this->成绩 < 乙.成绩;
} bool operator==(学生 甲, 学生 乙)
{
return 甲.学号 == 乙.学号;
}
} int main()
{
std::array<学生, > 成绩排序( min(甲, 乙), max(甲, 乙) );
}
如果 max 和 min 返回的都是甲,就完蛋了,给成绩排序居然把乙给搞没了。而这也就是 std::min() 和 std::max() 会导致的问题。
知乎问题: https://www.zhihu.com/question/266917342
stackoverflow : https://stackoverflow.com/questions/31974941/what-is-the-difference-between-equivalence-and-equality
为什么 max() 应该写成 b < a ? a : b 呢?的更多相关文章
- Python爬虫小实践:寻找失踪人口,爬取失踪儿童信息并写成csv文件,方便存入数据库
前两天有人私信我,让我爬这个网站,http://bbs.baobeihuijia.com/forum-191-1.html上的失踪儿童信息,准备根据失踪儿童的失踪时的地理位置来更好的寻找失踪儿童,这种 ...
- CSS Icon 项目地址 小图标-用css写成的
http://cssicon.space/#/icon/focus 这是所有用css写成的 小图标 右侧有 html和css代码
- HTML 5 背离贪吃蛇 写成了类似于屏幕校准
中间写了改 改了写 还是没做出自己满意的效果 ,看来自己的确不是一个走前端的料子.当然h5还是学一点好一点 具体说来 就是 在canvas 的画布中 鼠标点击后画上一个圆形 然后就有随机的在画布上面出 ...
- ASP.NET的一次奇遇:UserControl写成Control引发的w3wp进程崩溃
昨天在写代码中一不小心将UserControl写成了Control,将原来应该继承自System.Web.UI.UserControl的用户控件,比如下面的BlogStats: <%@ Cont ...
- Excel日期格式单元格写成yyyy.MM.dd格式将无法读取到DataTable
最近在改公司的订单系统,遇到了一个奇怪的问题.C#程序需要从Excel文件中将数据全部读取到DataTable,其中Excel文件的第一列是日期格式yyyy/MM/dd,而这一列中大部分的单元格都是按 ...
- 使用gfortran将数据写成Grads格式的代码示例
使用gfortran将数据写成Grads格式的代码示例: !-----'Fortran4Grads.f90' program Fortran4Grads implicit none integer,p ...
- 刺猬大作战(游戏引擎用Free Pascal写成,GUI用C++写成,使用SDL和Qt4)
游戏特性[编辑] 游戏引擎用Free Pascal写成,GUI用C++写成,使用SDL和Qt4[2]. 0.9.12开始支持实时动态缩放游戏画面. 个性化[编辑] 刺猬大作战有着高度定制性 游戏模式: ...
- Selenium爬取电影网页写成csv文件
绪论 首先写这个文章的时候仅仅花了2个晚上(我是菜鸟所以很慢),自己之前略懂selenium,但是不是很懂csv,这次相当于练手了. 第一章 环境介绍 具体实验环境 系统 Windows10教育版 1 ...
- indexOf刚开始写成IndexOf出错
{{# if(d.fronturlmin ==null||d.fronturlmin ==""){ }} <img src="@System.Configurati ...
随机推荐
- ElasticSearch(三)springboot整合ES
最基础的整合: 一.maven依赖 <parent> <groupId>org.springframework.boot</groupId> <artifac ...
- 细说JavaScript 导出 上万条Excel数据
首先这是个鸡肋的方法 合理的做法是 后端直接生成 前端给个链接就行了 (先说原因与过程最后上代码) 1. 先说说为什么会出现这个需求吧. : 在写运维网站时 自己粗略的看了一下bootstarp-ta ...
- Android中service的生命周期
Service作为Android四大组件 Service Activity ContentProvider BroadcastReceiver 之一,应用非常广泛,和Activity一样,Servic ...
- MVC + EFCore 项目实战 - 数仓管理系统8 - 数据源管理下--数据源预览
上篇我们完成了数据源保存功能,并顺便看了保存后的数据源列表展示功能. 本篇我们开始开发预览功能,用户预览主要步骤: 1.点击数据源卡片预览按钮 2.查看数据源包含的表 3.点击表名,预览表中数据 ...
- circle踢人(约瑟夫环) c++
这里更新指针法,真的每句都是坑 (寥寥数十句,句句都是坑) // // Created by snnnow on 2020/4/12. //question:转圈,一共N个人,数到M的出列,求最后一个 ...
- Markdown简洁语法说明
学于黑马和传智播客联合做的教学项目 感谢 黑马官网 传智播客官网 微信搜索"艺术行者",关注并回复关键词"乐优商城"获取视频和教程资料! b站在线视频 0.前言 ...
- goroutine调度源码阅读笔记
以下为本人阅读goroutine调度源码随手记的笔记,现在还是一个个知识点的形式,暂时还没整理,先发到这里,一点点更新: 1). runq [256]guintptr P 的runable队列最大 ...
- recv & recvfrom
#include <sys/types.h> #include <sys/socket.h> ssize_t recv(int sockfd, void *buf, size_ ...
- 利用mvc模式,实现用户的注册
实现功能:利用mvc模式,实现用户的登陆注册功能 1.程序的框架结构 2个包,bean,以及servlet 3个jsp页面,注册页面,注册成功页面,注册失败页面 mysql驱动 2.编程思想 通过js ...
- 【02python基础-函数,类】
1.函数中的全局变量与局部变量全局变量:在函数和类定义之外声明的变量.作用域为定义的模块,从定义位置开始到模块结束.全局变量降低了函数的通用性和可读性,要尽量避免全局变量的使用.全局边个两一般作为常量 ...