在最近发表的论文[1]中,Herb Sutter等人。用三向比较运算符描述编程语言C ++的扩展。作者提到了相当多的早期提案,所有这些提案最终都被拒绝了。
新方法的聪明核心概念是在比较运算符的返回类型中编码不同类别的关系。作者声明
主要设计目标是概念完整性[Brooks 1975],其中 意味着设计是连贯的,并且可靠地完成了用户的工作 期待它。
总共介绍了五类比较关系:
weak_equality
strong_equality
partial_ordering
weak_ordering
strong_ordering
如果您想知道,作者证明他们选择的词语如下:
我提出了这些名称,而不是标准的数学术语 相反,因为我发现这些更容易教。
从文本中可以推断出weak_equality
对应等价,strong_equality
对应等等,weak_ordering
对应弱订单,strong_ordering
到线性订单。与过去的好日子不同[3],遗憾的是,作者没有公理地描述这些术语。
对于partial_ordering
。
事实证明partial_ordering
与数学意义上的偏序不对应,因为它没有强加反对称。相反,它对应于准订单。
这两种关系都有实际应用。
广泛使用的部分顺序是子集关系。它显然是 reflexive , transitive 和反对称。特别是,给定A和B组的命题'A⊆B和B⊆A'意味着A和B是相等的(不仅仅是等价的)。
真正的悲剧是,在一个全新的框架中,不可能精确地表示子集关系(或任何其他部分顺序)。
对于浮点类型,我们使用支持两者的
partial_ordering
使用-0 <=> +0
的通常语义签署零和NaN 返回equivalent
,NaN <=> anything
返回unordered
。
虽然正确描述了特定浮点比较的结果,但作者忽略了这种结构根本没有定义任何顺序的事实,因为它缺乏反身性。数学废话?尝试对包含NaN的浮点实体数组进行排序,并享受未定义的行为!
2.5节介绍了进一步的漏洞。为每个比较关系类别提供默认模板函数实现,特别是:
strong_order()
weak_order()
partial_order()
strong_equal()
weak_equal()
由于此工具是纯库扩展,因此无需担心向后兼容性。作者声称
现有
operator<
通常会尝试表达弱势订单
显然,他们明白并非每一个现有的operator<
都表达了一个弱势的秩序。然而,weak_order()
的默认实现可以追溯到遗留operator==
和operator<
,因此可以提供充足的机会在运行时自己动手。
这完全破坏了提案的核心概念和光荣的设计目标。
根据[4],ISO C ++标准委员会(WG21)投票赞成在2017年11月的Albuquerque会议上将该提案纳入C ++ 20工作草案。
这引出了我的问题:委员会是否意识到了这些弱点并有意识地接受了这些弱点,或者只是忽略了它们?
确定:
参考
[1] Herb Sutter等人:一致的比较。 ISO / IEC JTC1 / SC22 / WG21文件P0515R2(前阿尔伯克基邮寄),2017-09-30。 网址https://wg21.link/p0515r2
[2] Walter E. Brown:宇宙飞船(比较)运营商的图书馆支持。 ISO / IEC JTC1 / SC22 / WG21文件P0768R0(前阿尔伯克基邮寄),2017-09-30。 网址https://wg21.link/p0768r0
[3] Hewlett-Packard公司:严格的弱订购。 标准模板库程序员指南,1994。 网址https://www.sgi.com/tech/stl/StrictWeakOrdering.html
[4] Botond Ballo:旅行报告。 2017年11月在阿尔伯克基举行的C ++标准会议。 网址https://botondballo.wordpress.com/2017/11/20/trip-report-c-standards-meeting-in-albuquerque-november-2017/
[5]维基百科:印第安纳皮比尔。 网址https://en.wikipedia.org/wiki/Indiana_Pi_Bill
补编
2.5节中命名比较函数的数学上合理的实现可以简单如下:
template <class T>
std::linear_ordering linear_order(const T& a, const T& b)
{
return compare_3way(a, b);
}
template <class T>
std::weak_ordering weak_order(const T& a, const T& b)
{
return compare_3way(a, b);
}
template <class T>
std::partial_ordering partial_order(const T& a, const T& b)
{
return compare_3way(a, b);
}
template <class T>
std::quasi_ordering quasi_order(const T& a, const T& b)
{
return compare_3way(a, b);
}
template <class T>
std::equality equal(const T& a, const T& b)
{
return compare_3way(a, b);
}
template <class T>
std::equivalence equivalent(const T& a, const T& b)
{
return compare_3way(a, b);
}
使用
template <class T, class U>
auto compare_3way(const T& a, const U& b)
{
return a <=> b;
}
或
template <class T, class U>
auto compare_3way(const T& a, const U& b)
{
if constexpr (/* can invoke a <=> b */)
{
return a <=> b;
}
else if constexpr (std::is_same_v<T, U> &&
/* can invoke a.M <=> b.M for each member M of T */)
{
/* do that */
}
}
或
template <class T, class U>
auto compare_3way(const T& a, const U& b)
{
if constexpr (/* can invoke a <=> b */)
{
return a <=> b;
}
else if constexpr (std::is_same_v<T, U> &&
/* can invoke compare_3way(a.M, b.M) for each member M of T */)
{
/* do that */
}
}