【问题标题】:Why use “b < a ? a : b” instead of “a < b ? b : a” to implement max template?为什么使用“b < a ? a : b" 而不是 "a < b ? b : a” 来实现最大模板?
【发布时间】:2018-11-22 20:26:45
【问题描述】:

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 &lt; a ? a : b” 而不是“a &lt; b ? b : a”

请注意,根据 [StepanovNotes] 的 max() 模板 故意返回“b

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

【问题讨论】:

  • 对我来说看起来不对...两个答案都是“正确的”,但如果 ab 等效,那么 !(a &lt; b) &amp;&amp; !(b &lt; a) 是正确的,所以 @ 987654332@ 和b &lt; a 都是假的,所以在b &lt; a ? a : b 中,b 被返回,这不是你想要的......你想要a &lt; b ? b : a
  • 如果您(重复)使用a = max(a, b);,您可能不想不必要地替换a
  • 顺便说一句,此模板应通过 const-references 获取参数并通过 const-reference 返回它们,否则,您将做一堆无用的副本(并且您将使用以下副本覆盖 a a)。
  • @Caleth:同时具有等价和等价的规范类型是 CaseInsensitiveString。对于该类型,既不是 astd::addressof 无关紧要。事实上,对于给定的T max(T a, T b),我们已经知道addressof(a) != addressof(b)
  • 你可以参考Stepano'v Notes on Programming for more details我在推特上看到这个因为解释不够详细。

标签: c++ templates


【解决方案1】:

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) 将返回一个 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 作为排序机制有意义的唯一方法是对象是否可互换。
【解决方案2】:

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

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


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

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

要求:类型 T 是 LessThanComparable(表 18)。

返回:较大的值。

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

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

特别是如果ab是等价的,那么a &lt; bb &lt; a都是false,所以:右边的值会在条件运算符中返回,所以@ 987654339@ 必须在右边,所以:

a < b ? b : a

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

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

【讨论】:

  • Godbolt link 解释了这个问题(感谢@songyuanyao 对X 的定义)。
  • @JackAidley 我已经编辑了答案以指定推理针对当前标准。
  • @codekaizer 我实际上指的是 “如果我们将 equiv(a, b) 定义为 !comp(a, b) && !comp(b, a)”。我已将链接更改为更好的报价(标准中的以下 3 行...)。
  • 惊讶地没有人提到浮点,其中 a&lt;bb&lt;a 都可能是假的,因为它们是无序的(一个或两个都是 NaN,所以 == 也是假的)。这可以看作是一种对等。松散相关:x86 的maxsd a, b 指令实现a = max(b,a) = b &lt; 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。
【解决方案3】:

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

如果它们等价,则返回a

所以应该使用a &lt; b ? b : a;另一方面,b &lt; 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 &lt; b) &amp;&amp; !(b &lt; a) 是真的,所以a &lt; bb &lt; a 是假的,所以...?
  • @ネロク 我想标准只是想让它确定;当它们相等时,应该返回哪一个。
  • 感谢您刷新我的记忆,了解为什么一个对象是“等价的”而不是“等价的”。
猜你喜欢
  • 2014-03-29
  • 2021-10-06
  • 1970-01-01
  • 2019-10-27
  • 2010-12-08
  • 2011-05-30
  • 2011-08-10
  • 1970-01-01
  • 2021-11-28
相关资源
最近更新 更多