Audience: EWG, CWG
S. Davis Herring <herring@lanl.gov>
Los Alamos National Laboratory
February 14, 2020
Special member functions may be defaulted in or out of their class:
class MysteryCopy {
int a,b,c,d,e,f,g;
public:
MysteryCopy()=default; // value initialization zeros
MysteryCopy(const MysteryCopy&); // not trivially copyable
};
MysteryCopy::MysteryCopy(const MysteryCopy&)=default;
// still not trivially copyable; equivalent to
// MysteryCopy::MysteryCopy(const MysteryCopy &m) /* noexcept(false) */
// : a(m.a),b(m.b),c(m.c),d(m.d),e(m.e),f(m.f),g(m.g) {}
The separate definition allows the programmer to control special properties of the function (constexpr
and inline
) and of the class (various kinds of triviality) without having to write a long definition manually (here, to copy each member). In particular, unless the out-of-class definition is declared inline
, it will typically appear in a separate translation unit and will provide ABI stability (as described in [dcl.fct.def.default]/5) and avoid duplicate code generation. It’s even possible to declare the function inline
, constexpr
, or consteval
in the class but provide its defaulted definition (and thus allow its use) only in certain translation units. (By contrast, the exception specification is controllable even in the class via noexcept(false)
.)
The situation for comparison functions is less clear. P0515R3 merely requires that a defaulted comparison be “declared in the member-specification of C”, which can be taken to allow =default
on a redeclaration of a friend. In fact, that paper goes further and mentions the possibility of access checks for non-member, non-friend defaulted operator<=>
. P0732R2 defines strong structural equality in terms of an operator<=>
defaulted in the definition of a class, as would be compatible with being able to do so elsewhere. P1185R2 preserves these wording details when separating ==
from <=>
and does not mention the possibility at all.
There is no record of any Evolution discussion of where to allow =default
since N3950, whose revisions N4114 and N4126 included “unanimously requested” support for non-members. P0432R0, which revived the syntax, retained that support.
However, the recollection of Core (at the January 16 issues-processing teleconference) is that the intent was to require that =default
be used only within a class for comparisons, and P2002R1 clarifies the rule to say so. Although P1907R1 removed the relevant class property of strong structural equality, this restriction does have the effect of making it impossible to default a comparison without making it inline (and possibly constexpr). This seems undesirable from an expressivity standpoint; one of the reasons for introducing default comparisons was to avoid writing error-prone repetitive code manually, but anyone who wants to control the properties of a comparison operator must do so (and potentially reimplement the synthesized three-way comparisons from [class.spaceship]/1). It also seems needlessly inconsistent with the other functions (the special members) that can be defaulted.
There is another reason for comparison functions to not be inline: the linkage concerns in P1498R1. Those issues matter little for defaulted special member functions, which principally call member functions of types that already must be available to any translation unit that can call the defaulted function. For that reason and for consistency reasons, P1779R3 does not affect the implicit inline
for functions defaulted in a class. However, it would not be at all unexpected for a defaulted comparison to invoke non-member subobject comparisons with internal linkage (because they are meaningless to clients). Such a comparison would be allowed by P1815R1 only if it were made opaque by being defined outside a class.
To allow defaulted comparisons to be non-inline (to control ABI or code generation), or inline but not constexpr (even if constexpr-compatible), allow their definition to appear outside the class that declares them (possibly as a friend). Retain the restriction that they be declared in the relevant class to avoid encouraging clients to add to a class’s interface (and to avoid questions of access).
Hidden friend comparison operators can be defaulted in an implementation translation unit, or in a private-module-fragment, to avoid being inline while remaining hidden. A member of, or an unbounded set of friends injected by, a class template cannot be so isolated, but clients must be able to generate code for them anyway.
Change paragraph 1:
A defaulted comparison operator function ([over.binary]) for some class
C
shall be a non-template functiondeclared in the member-specification ofthat isC
- a non-static const member of
C
having one parameter of typeconst C&
, or- a friend of
C
having either two parameters of typeconst C&
or two parameters of typeC
.
Change paragraph 3:
If the class definition does not explicitly declare an
==
operator function, but declares adefaultedthree-way comparison operator function as defaulted, an==
operator function is declared implicitly with the same access as the three-way comparison operator function. The implicitly-declared==
operator for a classX
is an inline member and is defined as defaulted in the definition ofX
. If the three-way comparison operator function is declared as a non-static const member, the implicitly-declared==
operator function is a member of the form[…]
Change paragraph 1:
A defaulted comparison operator function ([over.binary]) for some class
C
shall be a non-template functiondefined in the member-specification ofthat isC
- a non-static const non-volatile member of
C
having one parameter of typeconst C&
and either no ref-qualifier or the ref-qualifier&
, or- a friend of
C
having either two parameters of typeconst C&
or two parameters of typeC
.A
defaultedcomparison operator function for classC
that is defaulted on its first declaration and is not defined as deleted is implicitly defined when it is odr-used or needed for constant evaluation. Name lookups in the defaulted definition of a comparison operator function are performed from a context equivalent totheits function-bodyof the defaulted.operator@
functionA defaultedA definition of a comparison operatorfunction shall beas defaultedon itsthat appears in a class shall be the first declaration of that function.
Change paragraph 3 (editorially, for clarity):
If the member-specification does not explicitly declare any member or friend named
operator==
, an==
operator function is declared implicitly for eachdefaultedthree-way comparison operator function defined as defaulted in the member-specification, with the same access and function-definition and in the same class scope as the three-way comparison operator function, except that the return type is replaced withbool
and the declarator-id is replaced withoperator==
. [Note: […] — end note] [Example:[…]
— end example] [Note: […] — end note]
Thanks to Richard Smith for helping identify use cases.