ChatGPT解决这个技术问题 Extra ChatGPT

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

C++ Templates - The Complete Guide, 2nd Edition 引入了 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” 似乎对我有相同的结果。

在我看来,这两个答案都是“正确的”,但如果 ab 等价,则 !(a < b) && !(b < a) 为真,因此 a < bb < a 都是false,因此在 b < a ? a : b 中,返回 b,这不是您想要的……您想要 a < b ? b : a
如果您(重复)执行 a = max(a, b);,您可能不想不必要地替换 a
顺便说一句,此模板应通过 const-references 获取参数并通过 const-reference 返回它们,否则,您将做一堆无用的副本(并且您将使用 a 的副本覆盖 a)。
@Caleth:同时具有等价性和相等性的规范类型是 CaseInsensitiveString。对于那种类型,既不是a<A,也不是A<a。但 std::addressof 无关紧要。事实上,对于给定的 T max(T a, T b),我们已经知道 addressof(a) != addressof(b)
您可以参考Stepano'v Notes on Programming for more details我在阅读后发推文,因为解释不够详细。

T
T.C.

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

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


从那个链接“我很难责怪这样做的人:毕竟,他们只是遵循我编写的 max 的 C++ 标准规范。我花了几年时间才发现我错了。” - 哇!
你不能只做{min(a, b), max(b, a)}吗?
@CaptainMan:是的,但仍然不太明显。我认为 max(a,b) 将返回 a if-and-only-if min(a,b) 返回 b 并且 反之亦然 是合乎逻辑的,因此它们彼此相反并且 (无序)集合 {min(a,b), max(a,b)} 总是等于 {a,b}
@jpmc26:例如,如果按时间对事件列表进行排序,则无需关心排序操作是否稳定,只需关心每个事件在输入中恰好出现一次,同样在输出中也恰好出现一次。某些操作(例如查找和消除重复事件)可能需要使用完整的顺序,但在许多其他情况下,可以以任意顺序列出同时发生的事件,但不能重复或省略它们。
@supercat 在那种情况下将 minmax 应用于除 timestamp (排序键)之外的任何内容都是没有意义的。如果平等并不意味着可互换性,那么事件(对象)本身甚至不应该具有可比性。 {min(a, b), max(a, b)} 使 any 作为一种排序机制有意义的唯一方法是对象是否可互换。
C
Community

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

有关上下文解释,请参阅 @T.C.'s answer

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

模板 constexpr const T& max(const T& a, const T& b);要求:类型 T 是 LessThanComparable(表 18)。返回: 较大的值。备注:当参数相等时返回第一个参数。

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

特别是,如果 ab 等价,则 a < bb < a 都是 false,所以在条件运算符中会返回 : 右边的值,所以 a 有在右边,所以:

a < b ? b : a

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

因此,根据当前标准,您报价中的信息似乎是错误的,但定义它的上下文可能会有所不同。


Godbolt link 解释了问题(感谢 @songyuanyao 对 X 的定义)。
@JackAidley 我已经编辑了答案以指定推理针对当前标准。
@codekaizer 我实际上指的是“如果我们将 equiv(a, b) 定义为 !comp(a, b) && !comp(b, a)”。我已将链接更改为更好的报价(标准中的以下 3 行...)。
很惊讶没有人提到浮点,其中 a<bb<a 都可能是假的,因为它们是无序的(一个或两个都是 NaN,所以 == 也是假的)。这可以看作是一种对等。松散相关:x86 的 maxsd a, b 指令实现了 a = max(b,a) = b < a ? a : b。 (What is the instruction that gives branchless FP min and max on x86?)。该指令使源操作数(第二个)保持无序,因此如果有任何 NaN,则数组上的循环将为您提供 NaN。但 max_seen = max(max_seen, a[i]) 将忽略 NaN。
s
songyuanyao

关键是当它们相等时应该返回哪个;对于这种情况,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

如果 ab 是等价的,您能否详细说明为什么 std::max(a, b) 必须返回 a
@ネロク - It's just an arbitrary choice on the standard's part。虽然它是否是一个好的有点值得商榷。
只是我还是这与问题相矛盾?如果 ab 等价,则 !(a < b) && !(b < a) 为真,因此 a < bb < a 为假,所以...?
@ネロク 我想标准只是想让它确定;当它们相等时,应该返回哪一个。
感谢您刷新我对为什么一个对象“等效”但不“相等”的记忆。