<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>P2002R1: Defaulted comparison specification fixes</title>

<style type="text/css">
ins, ins > * { text-decoration:none; font-weight:bold; background-color:#A0FFA0 }
del, del > * { text-decoration:line-through; background-color:#FFA0A0 }

blockquote {
  color: #000000; background-color: #F1F1F1;
  border: 1px solid #D1D1D1;
  padding-left: 0.5em; padding-right: 0.5em;
}
blockquote.stdins {
  color: #000000; background-color: #C8FFC8;
  border: 1px solid #B3EBB3;
  padding-left: 0.5em; padding-right: 0.5em;
}
blockquote.stddel {
  text-decoration: line-through;
  color: #000000; background-color: #FFEBFF;
  border: 1px solid #ECD7EC;
  padding-left: 0.5em; padding-right: 0.5em;
}
</style>

</head>

<body>
Richard Smith<br>
2020-02-14<br>
P2002R1

<h2>Introduction</h2>

<p>
This paper provides a collection of wording fixes to improve the precision,
implementability, consistency, and completeness of the wording for defaulted
comparison operator functions.

<p>
In the following areas, there was previously no specification for how
a defaulted comparison operator behaves:

<ul>
<li>when it is implicitly defined (eagerly vs lazily);
<li>whether it is implicitly <tt>constexpr</tt>;
<li>when it may be explicitly declared <tt>constexpr</tt> or <tt>consteval</tt>.
</ul>

In each case, the new specification matches the behavior of
defaulted special member functions to the extent possible.

<p>
The rules for when to delete a comparison function were inconsistent
and varied between checks that overload resolution finds a usable function
and checks that require performing full semantic analysis on the definition
and determining whether it would be ill-formed.
Consistent with defaulted special member functions, we now look only for
surface-level problems: cases where overload resolution would fail
or the structure of the class is obviously incompatible with
a defaulted comparison (eg, reference members).

<p>
When determining whether a defaulted comparison is <tt>noexcept</tt>,
we now consider all operations it performs,
not only the invocation of comparison functions in its body.
This is consistent with the behavior of defaulted special member functions,
and avoids calls to <tt>terminate()</tt> if (for example)
a conversion performed when initializing a parameter of
a conversion function call throws an exception.

<p>
The specification for an implicitly-declared <tt>operator==</tt>
was incomplete, lacking specification for various properties of
the implicit declaration. Following prior CWG discussion, we
inherit all properties of the <tt>operator&lt;=&gt;</tt> onto the
<tt>operator==</tt> (other than the name and return type).

<p>
In addition, the rules for the defaulted operators other than
<tt>&lt;=&gt;</tt> and <tt>==</tt>
have been simplified and made to match the design intent,
that they capture the behavior of a rewritten operator
as a function whose address can be taken.
For those operators, we no longer care what members the class has,
since that plays no role in how the function is defaulted.

<h2>Wording</h2>

<p>
Change in 7.6.8 [expr.spaceship] paragraph 6:
</p>

<blockquote>
If at least one of the operands is of pointer type
<ins>and the other operand is of pointer or array type</ins>,
array-to-pointer conversions (7.3.2),
pointer conversions (7.3.11),
<del>function pointer conversions (7.3.13),</del>
and qualification conversions (7.3.5)
are performed on both operands to bring them to their composite pointer type (7.2.2).
<del>If at least one of the operands is of pointer-to-member type,
pointer-to-member conversions (7.3.12)
and qualification conversions (7.3.5)
are performed on both operands to bring them to their composite pointer type (7.2.2).
If both operands are null pointer constants, but not both of integer type,
pointer conversions (7.3.11)
are performed on both operands to bring them to their composite pointer type (7.2.2).
In all cases, after</del>
<ins>After</ins> the conversions, the operands shall have the same type.
[<i>Note</i>:
If both of the operands are arrays, array-to-pointer conversions (7.3.2) are not applied.
&mdash;<i>end note</i>]
</blockquote>

<i>
Drafting note: this extends the resolution of DR 583 to three-way comparisons,
and cleans up some dead wording left behind by the removal of <tt>std::*_equality</tt>.
The change to disallow three-way comparisons against null pointer constants
was approved by EWG as part of the discussion of P0946R0.
</i>

<p>
Change in 9.5.2 [dcl.fct.def.default] paragraph 3:
<p>

<blockquote>
An explicitly-defaulted function that is not defined as deleted
may be declared constexpr or consteval
only if it <del>would have been</del> <ins>is</ins> <del>implicitly declared as</del>
constexpr<ins>-compatible ([special], [class.compare.default])</ins>.
If a <ins>constexpr-compatible</ins> function is explicitly defaulted on its first declaration,
it is implicitly considered to be constexpr <del>if the implicit declaration would be</del>.
</blockquote>

<p>
Change in 10.6 [module.context] paragraph 2:
</p>

<blockquote>
During the implicit definition of a defaulted <del>special member</del> function
(11.4.3<ins>, 11.11.1 [class.compare.default]</ins>),
the instantiation context is the union of
the instantiation context from the definition of the class and
the instantiation context of the program construct
that resulted in the implicit definition of the <ins>defaulted</ins> <del>special member</del> function.
</blockquote>

<p>
Change in 10.6 [module.context] paragraph 4:
</p>

<blockquote>
During the implicit instantiation of a template
that is implicitly instantiated
because it is referenced from within
the implicit definition of
a defaulted <del>special member</del> function,
the instantiation context is
the instantiation context of
the defaulted <del>special member</del> function.
</blockquote>

<p>
Change in 11.11.1 [class.compare.default] paragraph 1:
</p>

<blockquote>
A defaulted comparison operator function (12.6.2) for some class C shall be a non-template function
<del>declared</del> <ins>defined</ins> in the member-specification of C that is
<ul>
<li>
a non-static const <ins>non-volatile</ins> member of C having one parameter of type const C& <ins>and
either no <i>ref-qualifier</i> or the <i>ref-qualifier</i> <tt>&amp;</tt></ins>, or

<li>
a friend of C having either two parameters of type const C& or two parameters of type C.
</ul>
<ins>
A defaulted comparison operator function for class <tt>C</tt>
that is not defined as deleted
is <i>implicitly defined</i>
<!-- at the end of the definition of class <tt>C</tt>. -->
when it is odr-used or needed for constant evaluation.
Name lookups in the defaulted definition are performed from a context equivalent to
the <i>function-body</i> of the defaulted <tt>operator@</tt> function.
A defaulted comparison operator function
shall be defaulted on its first declaration.</ins>
</blockquote>

<i>Drafting note: the lazy definition of the defaulted function
needs to perform unqualified lookup for operator functions.
Such lookups should be done at the point where the function is defaulted,
in a manner analogous to two-phase name lookup for templates.
</i>

<p>
<i>Drafting note: there is no need to require that
defaulted secondary operators be defaulted in their class
(or even that they have any particular parameter types).
Removing such restrictions seems like a design change
rather than a wording fix, so they are retained here.
</i>

<p>
<i>Drafting note: requiring a comparison to be defaulted
on its first declaration avoids cases where the meaning of
a program (such as whether a comparison is <tt>noexcept</tt>)
can change based on a simple reordering of top-level declarations.
</i>

<p>
Change in 11.11.1 [class.compare.default] paragraph 2:
</p>

<blockquote>
A defaulted <del>comparison</del> <ins><tt>&lt;=&gt;</tt> or <tt>==</tt></ins> operator function
for class <tt>C</tt>
is defined as deleted if
any non-static data member of <tt>C</tt> is of reference type or
<tt>C</tt> <del>is a union-like class</del> <ins>has variant members</ins> (11.5.1).
</blockquote>

<i>Drafting note: it is not necessary to consider members when defaulting
a secondary comparison operator (<tt>&lt;</tt>, <tt>&lt;=</tt>, ...)
in terms of a user-provided primary comparison operator.
Also, while variant members pose a problem for defaulted primary comparisons,
empty unions do not.</i>

<p>
Add two new paragraphs after 11.11.1 [class.compare.default] paragraph 2:
</p>

<blockquote class="stdins">
A binary operator expression <tt>a @ b</tt> is <i>usable</i> if either
<ul>
<li><tt>a</tt> or <tt>b</tt> is of class or enumeration type and
overload resolution (12.4) as applied to <tt>a @ b</tt> results in a usable candidate, or
<li>neither <tt>a</tt> nor <tt>b</tt> is of class or enumeration type and
<tt>a @ b</tt> is a valid expression.
</ul>
</blockquote>

<blockquote class="stdins">
A defaulted comparison function is <i>constexpr-compatible</i> if
it satisfies the requirements for a constexpr function ([dcl.constexpr]) and
no overload resolution performed when determining whether to delete the function
results in a usable candidate that is a non-constexpr function.
[<i>Note</i>:
This includes the overload resolutions performed:
<ul>
<li>
for an <tt>operator&lt=&gt;</tt> whose return type is not <tt>auto</tt>,
when determining whether a synthesized three-way comparison is defined,
<li>
for an <tt>operator&lt;=&gt;</tt> whose return type is <tt>auto</tt>
or for an <tt>operator==</tt>,
for a comparison between an element of the expanded list of subobjects and itself,
or
<li>
for a secondary comparison operator <tt>@</tt>,
for the expression <tt>x @ y</tt>.
</ul>
&mdash;<i>end note</i>]
</blockquote>

<i>Drafting note: the implicit definition can invoke other functions, such as a conversion from
some custom comparison category to the return type, or an <tt>operator@</tt> if the use of the <tt>@</tt>
operator is rewritten in terms of a different operator. Ignoring those, and considering only
the results of overload resolutions that affect deletedness, is consistent with
the rule we use for defaulted special member functions. It doesn't really matter if we get this "wrong"
in a corner case, since the worst case is that a comparison function is spuriously constexpr.</i>

<p>
Change in 11.11.1 [class.compare.default] paragraph 3:
</p>

<blockquote>
If the <del>class definition</del> <ins><i>member-specification</i></ins>
does not explicitly declare
<del>an <tt>==</tt> operator function</del>
<ins>any member or friend named <tt>operator==</tt></ins>,
<del>but declares a defaulted three-way comparison operator function,</del>
an <tt>==</tt> operator function is declared implicitly
<ins>for each defaulted three-way comparison operator function
defined in the <i>member-specification</i>,</ins>
with the same access <ins>and <i>function-definition</i>
and in the same class scope</ins>
as the <ins>respective</ins> three-way comparison operator function<ins>,
except that the return type is replaced with <tt>bool</tt>
and the <i>declarator-id</i> is replaced with <tt>operator==</tt></ins>.
<ins>[<i>Note</i>: Such an</ins>
<del>The</del> implicitly-declared <tt>==</tt> operator for a class <tt>X</tt>
is an inline <del>member</del> <ins>function</ins> and
is defined as defaulted in the definition of <tt>X</tt><ins>,
and has the same
<i>parameter-declaration-clause</i> and
trailing <i>requires-clause</i> as the respective three-way comparison operator</ins>.
If the three-way comparison operator function is declared as
a non-static const member, the implicitly-declared <tt>==</tt> operator function is a <ins>non-static const</ins> member<ins>.</ins>
<del>of the form
<pre>
bool X::operator==(const X&) const;
</pre>
Otherwise,
</del>
<ins>If the three-way comparison operator function declaration is a friend declaration,</ins>
the implicitly-declared <tt>==</tt> operator function is
<ins>a friend declaration;</ins>
<del>
of the form
<pre>
friend bool operator==(const X&, const X&);
</pre>
</del>
<del>[<i>Note</i>: Such</del> <ins>such</ins> a friend function is visible to argument-dependent lookup only.
<ins>If the three-way comparison operator function is declared
<tt>virtual</tt>, <tt>constexpr</tt>, or <tt>consteval</tt>,
the implicitly-declared <tt>==</tt> operator function is declared
<tt>virtual</tt>, <tt>constexpr</tt>, or <tt>consteval</tt>,
respectively.
If the three-way comparison operator function has no <i>noexcept-specifier</i>,
the implicitly-declared <tt>==</tt> operator function has
an implicit exception specification ([except.spec])
that may differ from the implicit exception specification of
the three-way comparison operator function.
</ins> &mdash;<i>end note</i>]
<ins>[<i>Example</i>:
<pre>
template&lt;typename T&gt; struct X {
  friend constexpr std::partial_ordering operator&lt;=&gt;(X, X) requires (sizeof(T) != 1) = default;
  // implicitly declares: friend constexpr bool operator==(X, X) requires (sizeof(T) != 1) = default;

  [[nodiscard]] virtual std::strong_ordering operator&lt;=&gt;(const X&amp;) const = default;
  // implicitly declares: [[nodiscard]] virtual bool operator==(const X&amp;) const = default;
};
</pre>
&mdash;<i>end example</i>]</ins>
<del>The operator is a <tt>constexpr</tt> function if its definition would satisfy the requirements for a <tt>constexpr</tt> function.</del>
[<i>Note</i>: The <tt>==</tt> operator function is declared implicitly even if the defaulted three-way comparison operator function is defined as deleted. &mdash;<i>end note</i>]
</blockquote>

<i>Drafting note: there can be more than one defaulted three-way comparison,
and we previously agreed to produce a defaulted <tt>operator==</tt> for each.
The old wording didn't specify many of the properties of the <tt>operator==</tt>
function; inheriting them all from the <tt>operator&lt;=&gt;</tt> function
seems like the right default behavior.</i>

<p>
Change in 11.11.1 [class.compare.default] paragraph 4:
</p>

<blockquote>
[...]
<del>It is unspecified whether virtual base class subobjects
appear more than once in the expanded list of subobjects.</del>
</blockquote>

<p>
Change heading of 11.11.2 [class.eq]: Equality operator<del>s</del>
</p>

<p>
Change in 11.11.2 [class.eq] paragraph 2:
</p>

<blockquote>
A defaulted <tt>==</tt> operator function
for a class <tt>C</tt> is defined as deleted unless,
for each <tt>x</tt><sub>i</sub> in the expanded list of subobjects
for an object <tt>x</tt> of type <tt>C</tt>,
<tt>x</tt><sub>i</sub> <tt>==</tt> <tt>x</tt><sub>i</sub>
<ins>is usable ([class.compare.default])</ins>
<del>is a valid expression and contextually convertible to <tt>bool</tt></del>.
</blockquote>

<i>Drafting note: if the return type is not contextually convertible to bool, this will result in a hard error.</i>

<p>
Delete 11.11.2 [class.eq] paragraph 4:
</p>

<blockquote class="stddel">
A defaulted != operator function for a class C with parameters x and y is defined as deleted if
<ul>
<li> overload resolution (12.4), as applied to x == y, does not result in a usable function, or
<li> x == y is not a prvalue of type bool.
</ul>
Otherwise, the operator function yields !(x == y).
</blockquote>

<p>
Change in 11.11.2 [class.eq] paragraph 5:
</p>

<blockquote>
<pre>
struct D {
  int i;
  friend bool operator==(const D& x, const D& y) = default; // OK, returns x.i == y.i
  <del>bool operator!=(const D& z) const = default; // OK, returns !(*this == z)</del>
};
</pre>
</blockquote>

<i>Drafting note: <tt>!=</tt> is handled with all the other secondary operators.</i>

<p>
Change in 11.11.3 [class.spaceship] paragraph 1:
</p>

<blockquote>
[...]
<ul>
<li>If <del>overload resolution for</del> <tt>a &lt;=&gt; b</tt> <del>results in a</del> <ins>is</ins> usable <ins>([class.compare.default])</ins>
<del>function (12.4)</del>, <tt>static_cast&lt;R&gt;(a &lt;=&gt; b)</tt>.
<li>Otherwise, if overload resolution for <tt>a &lt;=&gt; b</tt> <ins>is performed and</ins> finds at least one viable candidate, the synthesized
three-way comparison is not defined.
<li><ins>Otherwise, if <tt>R</tt> is not a comparison category type, or
either the expression <tt>a == b</tt> or the expression <tt>a &lt; b</tt>
is not usable, the synthesized three-way comparison is not defined.</ins>
<li>[...]
<li>Otherwise<del>, if</del> <ins>(when</ins> <tt>R</tt> is <tt>partial_ordering</tt><ins>)</ins>, <del>then</del> [...]
<li><del>Otherwise, the synthesized three-way comparison is not defined.</del>
</ul>
[<i>Note</i>: A synthesized three-way comparison may be ill-formed if overload resolution finds usable <del>functions</del> <ins>candidates</ins>
that do not otherwise meet the requirements implied by the defined expression. &mdash;<i>end note</i>]
</blockquote>

<i>Drafting note: a built-in candidate is not a function, but must be considered here.</i>

<p>
Change in 11.11.3 [class.spaceship] paragraph 2:
</p>

<blockquote>
Let <tt>R</tt> be the declared return type of a defaulted three-way comparison operator function<del>.
Given</del>
<ins>, and let <tt>x</tt><sub>i</sub> be the elements of the</ins>
<del>an</del> expanded list of subobjects for an object x of type C<del>,
let R<sub>i</sub> be the type of the expression x<sub>i</sub> &lt;=&gt; x<sub>i</sub>,
or void if overload resolution applied to that expression does not find a usable function</del>.
<ul>
<li>
If <tt>R</tt> is <tt>auto</tt>, then
<ins>
let <i>cv<sub>i</sub></i> <tt>R</tt><sub>i</sub> be the type of the expression <tt>x</tt><sub>i</sub> <tt>&lt;=&gt;</tt> <tt>x</tt><sub>i</sub>.
The operator function is defined as deleted if that expression is not usable or if <tt>R</tt><sub>i</sub> is not a comparison category type ([cmp.categories.pre] 17.11.2.1)
for any i.
The</ins>
<del>the</del> return type is deduced as the common comparison type (see below) of R<sub>0</sub>, R<sub>1</sub>, &hellip;, R<sub>n-1</sub>.
<del>If the return type is deduced as void, the operator function is defined as deleted.</del>

<li>
Otherwise, <ins><tt>R</tt> shall not contain a placeholder type. If</ins> <del>if</del>
the synthesized three-way comparison of type <tt>R</tt>
between any objects <tt>x</tt><sub>i</sub> and <tt>x</tt><sub>i</sub> is not
defined <del>or would be ill-formed</del>, the operator function is defined as deleted.
</ul>
</blockquote>

<p>
Change in 11.11.3 [class.spaceship] paragraph 3:
</p>

<blockquote>
The return value V of type R of the defaulted three-way comparison operator function
with parameters x and y of the same type
is determined by
comparing corresponding elements xi and yi
in the expanded lists of subobjects for x and y (in increasing index order)
until the first index i where
the synthesized three-way comparison of type R between xi and yi
yields a result value vi where vi != 0, contextually converted to bool, yields true;
V is <ins>a copy of</ins> vi.
If no such index exists, V is static_cast<R>(std::strong_ordering::equal).
</blockquote>

<i>Drafting note: the existing wording is implementable,
by constructing each v<sub>i</sub> in turn in the function's return slot,
but the original design specified that a copy of the first non-zero return value
is to be returned, not the value itself.</i>

<p>
Change in 11.11.3 [class.spaceship] paragraph 4:
</p>

<blockquote>
The common comparison type U of a possibly-empty list of n <ins>comparison category</ins> types T<sub>0</sub>, T<sub>1</sub>, &hellip;, T<sub>n-1</sub> is defined as follows:
<ul>
<li><del>If any Ti is not a comparison category type (17.11.2), U is void.</del>
<li><del> Otherwise, if</del> <ins>If</ins> at least one Ti is std::partial_ordering, U is std::partial_ordering (17.11.2.2).
<li>[...]
</ul>
</blockquote>

<i>Drafting note: producing <tt>void</tt> here is a hack that exists
to produce some answer from <tt>std::common_comparison_category</tt>,
and should be localized to that trait.</i>

<p>
Change heading and section label of 11.11.4 [class.<del>rel</del><ins>compare.secondary</ins>]: <del>Relational</del> <ins>Secondary comparison</ins> operators
</p>

<p>
Change in 11.11.4 [class.rel] paragraph 1:
</p>

<blockquote>
<ins>A <i>secondary comparison operator</i> is a relational operator ([expr.rel]) or the <tt>!=</tt> operator.</ins>
A defaulted <del>relational</del> operator function (12.6.2) for <del>some</del> <ins>a secondary comparison</ins> operator <tt>@</tt>
shall have a declared return type <tt>bool</tt>.
</blockquote>

<p>
Change in 11.11.4 [class.rel] paragraph 2:
</p>

<blockquote>
The operator function with parameters x and y is defined as deleted if
<ul>
<li>overload resolution (12.4), as applied to <tt>x <del>&lt;=&gt;</del> <ins>@</ins> y</tt>, does not result in a usable <del>function</del> <ins>candidate</ins>, or
<li>the <ins>candidate selected by overload resolution is not a rewritten candidate</ins> <del>operator @ cannot be applied to the return type of x &lt;=&gt; y</del>.
</ul>
Otherwise, the operator function yields <tt>x <del>&lt;=&gt;</del> <ins>@</ins> y <del>@ 0</del></tt>.
<ins>
The defaulted operator function is not considered as a candidate in the overload resolution for the <tt>@</tt> operator.
</ins>
</blockquote>

<p>
Add a new paragraph to the end of 11.4.3 [special]:
</p>

<blockquote class="stdins">
A defaulted special member function is <i>constexpr-compatible</i> if
the corresponding implicitly-declared special member function
would be a constexpr function.
</blockquote>

<p>
Change in 12.4 [over.match] paragraph 4:
</p>

<blockquote>
Overload resolution results in a <i>usable <del>function</del> <ins>candidate</ins></i>
if overload resolution succeeds and the selected <ins>candidate is either not a</ins>
function <ins>([over.built]), or is a function that</ins>
is not deleted and is accessible from the context in which overload resolution was performed.
</blockquote>

<p>
Change in 14.5 [except.spec] paragraph 11:
</p>

<blockquote>
The exception specification for a comparison operator function (12.6.2) without a noexcept-specifier that
is defaulted on its first declaration is potentially-throwing if and only if <del>the invocation of any
comparison operator</del> <ins>any expression</ins> in the implicit definition is potentially-throwing.
</blockquote>

<i>Drafting note: the implicit definition can invoke other functions, such as a conversion from
some custom comparison category to the return type. Considering those is consistent with
the rule we use for defaulted special member functions.</i>

<p>
Change in 14.5 [except.spec] paragraph 13:
</p>

<blockquote>
An exception specification is considered to be <i>needed</i> when:
<ul>
<li>[&hellip;]
<li>the exception specification is needed for a defaulted <del>special member</del>
function that calls the function. [<i>Note</i>: &hellip;]
</ul>
The exception specification of a defaulted <del>special member</del> function
is evaluated as described above only when needed;
similarly, the <i>noexcept-specifier</i> of a specialization
of a function template or member function of a class template
is instantiated only when needed.
</blockquote>

<p>
Change in 17.11.3 [cmp.common] paragraph 2:
<p>

<blockquote>
<i>Remarks</i>:
The member <i>typedef-name</i> <tt>type</tt> denotes the common comparison type (11.11.3) of
<tt>Ts...</tt>, the expanded parameter pack<ins>,
or <tt>void</tt> if any element of <tt>Ts</tt> is not a comparison category type</ins>.
[<i>Note</i>: This is <del>well-defined even</del> <ins><tt>std::strong_ordering</tt></ins>
if the expansion is empty<del> or includes a
type that is not a comparison category type</del>. &mdash;<i>end note</i>]
</blockquote>

</body></html>
