为什么使用“ b <a?a:b”而不是“ a <b?b:a”以实现最大模板?


154

C ++模板-《完整指南,第二版》介绍了max模板:

template<typename T>
T max (T a, T b)
{
  // if b < a then yield a else yield b
  return  b < a ? a : b;
}

它解释了使用“b < a ? a : b”代替“a < b ? b : a”

请注意,根据[StepanovNotes]的max()模板有意返回“ b <a?a:b”而不是“ a <b?b:a”,以确保即使两个值相等但不相等,该函数也能正确运行。

如何理解“ even if the two values are equivalent but not equal.”?“a < b ? b : a”似乎对我有相同的结果。


8
看起来我错了......这两个答案都是“正确的”,但如果ab相等的,则!(a < b) && !(b < a)是真实的,所以a < bb < a都是假的,所以b < a ? a : bb被返回,这是不是你想要的。你想a < b ? b : a
Holt

1
您通常可以区分等价的ab与等价的std::addressof。等
卡莱斯(Caleth),

14
如果这样做a = max(a, b);(反复),则可能不想a不必要地进行更换。
Bo Persson

2
顺便说一句,此模板应通过const-references获取参数,并通过const-reference返回参数,否则,您将执行一堆无用的副本(并且将a使用的副本覆盖a)。
Holt

3
@Caleth:具有相等性和相等性的规范类型是CaseInsensitiveString。对于该类型,a <A和A <a均不。但是std::addressof无关紧要。实际上,对于给定的T max(T a, T b)我们已经知道addressof(a) != addressof(b)
MSalters

Answers:


150

std::max(a, b)确实指定a当两者相等时返回。

Stepanov和其他人认为这是一个错误,因为它破坏了给定的有用属性,a并且b您始终可以将它们排序{min(a, b), max(a, b)};为此,您需要在参数相等时max(a, b)返回b


48
通过该链接“我很难责怪这样做的人:毕竟,他们只是遵循我编写的max的C ++标准规范。我花了好几年的时间才发现自己错了。” - 哇!
杰克·艾德利

23
你不能做{min(a, b), max(b, a)}吗?
曼队长

12
@CaptainMan:是的,但是它仍然不那么明显。我会认为,逻辑上的意义是max(a,b)返回一个if-and-if-if min(a,b)返回b,反之亦然,以便它们彼此相反,并且(无序)集合{min(a,b), max(a,b)}始终等于{a,b}
杰克·艾德利

5
@ jpmc26:例如,如果要按时间对事件列表进行排序,则不必关心排序操作是否稳定,从而不必担心每个事件在输入中只出现一次,同样在输出中只出现一次。某些操作(例如查找和消除重复的事件)可能需要使用完整的顺序,但是在许多其他情况下,可以以任意顺序列出同时发生的事件,但不能重复或忽略它们是可以接受的。
超级猫

2
@supercat 在那种情况下,除了timestamp(排序键)之外,将minmax都应用是没有任何意义的。如果相等并不意味着可互换性,那么事件(对象)本身甚至不应该是可比较的。唯一的办法,使任何意义,因为一种机制是,如果对象是可以互换的。{min(a, b), max(a, b)}
jpmc26

62

这个答案解释了为什么给定的代码从C ++标准的角度来看是错误的,但是却没有上下文。

有关上下文的说明,请参见@TC的答案


该标准定义std::max(a, b)如下[alg.min.max](重点是我的):

template<class T> constexpr const T& max(const T& a, const T& b);

要求:类型T小于可比较的(表18)。

返回:较大的值。

备注当参数相等时,返回第一个参数。

等效在这里意味着!(a < b) && !(b < a)true [alg.sorting#7]

特别是,如果ab相等,则两个a < bb < a都是false,因此右侧的值:将在条件运算符中返回,因此a必须在右侧,因此:

a < b ? b : a

...似乎是正确的答案。这是libstdc ++libc ++使用的版本。

因此,根据当前的标准,您引用的信息似乎有误,但是定义该信息的上下文可能有所不同。


4
解释问题的Godbolt链接(感谢@songyuanyao的定义X)。
Holt

1
@JackAidley我已经编辑了答案,以指定推理针对当前标准。
Holt

@codekaizer我实际上指的是“如果我们将equiv(a,b)定义为!comp(a,b)&&!comp(b,a)”。我已将链接更改为更好的报价(标准下面的3行...)。
Holt

1
惊讶的是没有人提到浮点,其中a<bb<a都可能是错误的,因为它们是无序的(一个或两个都是NaN,所以==也是错误的)。这可以看作是等效的。松散相关:x86的maxsd a, b指令实现a = max(b,a) = b < a ? a : b。(在x86上给出无分支FP最小值和最大值的指令是什么?)。该指令使源操作数(第二个)保持无序,因此,如果存在任何NaN,则遍历数组的循环将为您提供NaN。但是max_seen = max(max_seen, a[i])会忽略NaN。
彼得·科德斯


21

关键是当它们相等时应该返回哪一个。在这种情况下std::max必须返回a(即第一个参数)。

如果它们相等,则返回a

所以a < b ? b : a应该使用;另一方面,b < a ? a : b;b错误地返回。

(正如@Holt所说,引用似乎相反。)

“这两个值相等但不相等”表示在比较时它们具有相同的值,但在某些其他方面它们却是不同的对象。

例如

struct X { int a; int b; };
bool operator< (X lhs, X rhs) { return lhs.a < rhs.a; }
X x1 {0, 1};
X x2 {0, 2};
auto x3 = std::max(x1, x2); // it's guaranteed that an X which cantains {0, 1} is returned

1
您能否详细说明为什么std::max(a, b)必须返回a,如果ab相等?
眠りネロク

4
@ネロク- 这只是标准方面的任意选择。虽然这是一个好问题,但值得商de。
StoryTeller-Unslander Monica

是我还是这与问题矛盾?如果ab是等价的,!(a < b) && !(b < a)则为true,所以a < band b < a为假,所以...?
Holt

2
@ネロク我想这个标准只是想让它确定;当它们相等时应返回哪一个。
songyuanyao

1
感谢您刷新我的记忆,为什么一个对象将是“等效的”而不是“相等的”。
杰夫·沃克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.