In a recently published paper [1], Herb Sutter et al. describe an extension of the programming language C++ by a three way comparison operator. The authors refer to a considerable number of earlier proposals, all of which were ultimately rejected.
The clever core concept of the new approach is to encode different categories of relations in the return type of the comparison operator. The authors declare that
The primary design goal is conceptual integrity [Brooks 1975], which means that the design is coherent and reliably does what the user expects it to do.
A total of five categories of comparison relations are introduced:
weak_equality
strong_equality
partial_ordering
weak_ordering
strong_ordering
If you are wondering, the authors justify their choice of words as follows:
I proposed these names instead of the standard mathematical terms instead, because I have found that these are simpler to teach.
From the text it can be deduced that weak_equality
corresponds to equivalence, strong_equality
to equality, weak_ordering
to weak order, and strong_ordering
to linear order. Unlike in the good old days [3], the authors unfortunately do not describe these terms axiomatically.
This would have been particularly helpful in the case of partial_ordering
.
It turns out that partial_ordering
does not correspond to a partial order in the mathematical sense, since it does not impose anti-symmetry. Rather, it corresponds to a quasi-order.
Both types of relations have practical applications.
A widely used partial order is the subset relation. It is evidently reflexive, transitive, and anti-symmetric. Particularly, given sets A and B the proposition 'A ⊆ B and B ⊆ A' implies that A and B are equal (not just equivalent).
It is a real tragedy that in a brand-new framework it is not possible to precisely represent the subset relation (or any other partial order).
For floating point types, we use
partial_ordering
which supports both signed zero and NaNs, with the usual semantics that-0 <=> +0
returnsequivalent
andNaN <=> anything
returnsunordered
.
While the results of particular floating point comparisons are correctly depicted, the authors neglect the fact that this construction does not define any order at all, since it lacks reflexivity. Mathematical nonsense? Try sorting an array of floating point entities that contains NaN and enjoy undefined behavior!
Further loopholes are introduced in section 2.5. A default template function implementation is provided for each of the comparison relation categories, in particular:
strong_order()
weak_order()
partial_order()
strong_equal()
weak_equal()
Since this facility is a pure library extension, there is no need to worry about backward compatibility. The authors claim that
existing
operator<
usually tries to express a weak order
Clearly, they understand that not every existing operator<
actually expresses a weak order. Nevertheless, the default implementation of weak_order()
falls back to legacy operator==
and operator<
, and thus offers ample opportunity to shoot yourself in the foot at runtime.
This completely undermines the core concepts and the honorable design goals of the proposal.
According to [4], the ISO C++ Standards Committee (WG21) voted in favor of including the proposal in the C++20 Working Draft at its November 2017 meeting in Albuquerque.
This leads to my question: Is the committee aware of the weak points and consciously accepts them or has it simply overlooked them?
To be sure:
- I like the basic concept very much.
- I don't see any benefits in changing long-established terms.
- I don't like changing the meaning of common designations in silence without any clear indication.
- I find it a pity that it is not intended to precisely represent partial orders.
- I find it unacceptable to unnecessarily expose users to the risk of latent runtime errors.
- A direct message to one of the authors a few months ago remained unanswered and disregarded.
References
[1] Herb Sutter, et al.: Consistent comparison. ISO/IEC JTC1/SC22/WG21 document P0515R2 (pre-Albuquerque mailing), 2017–09–30. URL https://wg21.link/p0515r2
[2] Walter E. Brown: Library Support for the Spaceship (Comparison) Operator. ISO/IEC JTC1/SC22/WG21 document P0768R0 (pre-Albuquerque mailing), 2017–09–30. URL https://wg21.link/p0768r0
[3] Hewlett-Packard Company: Strict Weak Ordering. Standard Template Library Programmer's Guide, 1994. URL https://www.sgi.com/tech/stl/StrictWeakOrdering.html
[4] Botond Ballo: Trip Report. C++ Standards Meeting in Albuquerque, November 2017. URL https://botondballo.wordpress.com/2017/11/20/trip-report-c-standards-meeting-in-albuquerque-november-2017/
[5] Wikipedia: Indiana Pi Bill. URL https://en.wikipedia.org/wiki/Indiana_Pi_Bill
Supplement
A hierarchy that contains true partial orders could look like:
A mathematically sound implementation of the named comparison functions from section 2.5 could be as simple as:
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);
}
with either
template <class T, class U>
auto compare_3way(const T& a, const U& b)
{
return a <=> b;
}
or
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 */
}
}
or
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 */
}
}
来源:https://stackoverflow.com/questions/47485803/how-and-why-did-the-iso-c-standards-committee-wg21-decide-to-accept-the-prop