Skip to content

[class.compare.default] p1 The condition of implicitly defining a defaulted comparison operator function CWG2546 #5336

Description

@xmh0511

[class.compare.default] p1 just says

A comparison operator function for class C 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.

Consider this example

struct HasNoLessThan { };
struct C{
    friend HasNoLessThan operator <=>(C const&, C const&);
    bool operator <(C const&) const /*noexcept*/ = default;
};
int main(){
   using ptr = decltype(&C::operator<); // #1
}

Since &C::operator< appears as an unevaluated operand, function C::operator < is not odr-used by this expression. However, Clang reports a diagnosis that:

in evaluation of exception specification for 'C::operator<' needed here

[except.spec] p11 says

The exception specification for a comparison operator function ([over.binary]) without a noexcept-specifier that is defaulted on its first declaration is potentially-throwing if and only if any expression in the implicit definition is potentially-throwing.

So, It is reasonable to check the definition of the defaulted function. However, we just define two contexts that can cause the implicit definition for the defaulted function. Checking the expressions in the implicit definition is not defined to be odr-used nor constant evaluation of the function. It seems that we lack the case that determines the exception specification of a defaulted comparison operator function.

Improvement:

A comparison operator function for class C that is defaulted on its first declaration and is not defined as deleted is implicitly defined when it is odr-used, needed for constant evaluation, or needed to determine exception specification(if necessary).

Activity

  1. frederick-vs-ja commented on Mar 7, 2022

    @frederick-vs-ja
    Contributor

    [dcl.fct.def.delete]/2

    A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.

    So even if the implicit exception specification is not needed, decltype(&C::operator<) is ill-formed.

    IIUC this is a bug of clang. Please report it here.

  2. xmh0511 commented on Mar 7, 2022

    @xmh0511
    ContributorAuthor

    [dcl.fct.def.delete]/2

    A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.

    So even if the implicit exception specification is not needed, decltype(&C::operator<) is ill-formed.

    IIUC this is a bug of clang. Please report it here.

    No, the defaulted function is not deleted. HasNoLessThan operator <=>(C const&, C const&) is a usable candidate and a rewritten candidate for x < y, as per [class.compare.secondary] p2 and [over.match.oper#3.4]. Instead, I think it is a bug of GCC, which says that defaulted operator function is deleted.

  3. frederick-vs-ja commented on Mar 7, 2022

    @frederick-vs-ja
    Contributor

    Are you explaining why the comment in [class.compare.secondary] p3 is wrong? I believe it's right, extremely.

    When the rewritten candidate is found, a nested overload resolution is performed for the written expression and then fails. According to [over.match.general] p4, the rewritten candidate is not usable.

  4. xmh0511 commented on Mar 7, 2022

    @xmh0511
    ContributorAuthor

    Are you explaining why the comment in [class.compare.secondary] p3 is wrong? I believe it's right, extremely.

    When the rewritten candidate is found, a nested overload resolution is performed for the written expression and then fails. According to [over.match.general] p4, the rewritten candidate is not usable.

    "the rewritten candidate is not usable" No, it is usable. First, HasNoLessThan operator <=>(C const&, C const&) is a candidate for x < y, and all operands of the expression can be as the arguments in the call of HasNoLessThan operator <=>(C const&, C const&) since the operands are of type const C. According to [over.match.general] p2 and [over.match.general] p3, HasNoLessThan operator <=>(C const&, C const&) is the unique best viable function. Hence, the overload resolution succeeds.

    a nested overload resolution is performed for the written expression and then fails.

    IMO, overload resolution only considers the candidate itself. It shouldn't consider subsequent interpretation for the expression. The clue is here:

    If a rewritten operator<=> candidate is selected by overload resolution for an operator @, x @ y is interpreted as...

    That is, only after the overload resolution for the expression x@y has succeeded and selected rewritten operator<=> candidate will further interpretation be done. [class.compare.secondary] p2 just requires that

    overload resolution ([over.match]), as applied to x @ y, does not result in a usable candidate

    It didn't say the reinterpretation of x < y, in this case, which is (x <=> y) < 0, shall have a usable candidate or be well-formed.


    [over.match.general] p1 states that

    Overload resolution is a mechanism for selecting the best function to call given a list of expressions that are to be the arguments of the call and a set of candidate functions that can be called based on the context of the call.

    For expression x < y, the set of candidate functions is { HasNoLessThan operator <=>(C const&, C const&) } as per [over.match.oper] p3 and the list of arguments are x, y as per [over.match.oper] p7. Anyway, the overload resolution can succeed.

  5. xmh0511 commented on Mar 7, 2022

    @xmh0511
    ContributorAuthor

    If the comment is the intent of [class.compare.secondary] p2, it may be improved to

    The operator function with parameters x and y is defined as deleted if

    • the candidate selected by overload resolution, as applied to x @ y, is not a rewritten candidate, or
    • For expression x @ y, it is ill-formed.

    the candidate selected by overload resolution for x < y is HasNoLessThan operator <=>(C const&, C const&), which is a rewritten candidate, the first bullet is not violated. Since the selected candidate is a rewritten operator<=> candidate, the expression x < y is interpreted as (x <=> y) < 0, which is ill-formed; the first operand of < has type HasNoLessThan while the second has the type int, they are not comparable.

  6. xmh0511 commented on Mar 7, 2022

    @xmh0511
    ContributorAuthor

    a nested overload resolution is performed for the written expression and then fails.

    Another example that can prove we shouldn't augment the extent of considering overload resolution for this definition is:

    struct HasNoLessThan { 
        operator int(){
            return 0;
        }
    };
    struct C{
        friend HasNoLessThan operator <=>(C const&, C const&);
        bool operator <(C const&) const = default;
    };
    int main(){
       using ptr = decltype(&C::operator<);
    }

    Both implementations accept this example. The nested overload resolution will be performed for the operator < within (x<=>y) < 0. The selected candidate is the built-in one, which would violate the second bullet in [class.compare.secondary] p2. It is not reasonable to augment the extent of the overload resolution.

  7. jensmaurer commented on Mar 7, 2022

    @jensmaurer
    Member

    The "selected candidate" in [class.compare.secondary] p2 is operator <=>, not the second-order overload resolution for operator<.

    The fixes in CWG2546 should address this, too.

  8. changed the title [-][class.compare.default] p1 The condition of implicitly defining a defaulted comparison operator function[/-] [+][class.compare.default] p1 The condition of implicitly defining a defaulted comparison operator function CWG2546[/+] on Mar 7, 2022
  9. frederick-vs-ja commented on Apr 28, 2024

    @frederick-vs-ja
    Contributor

    Should have been fixed by #6906 (eef4c2b).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    cwgIssue must be reviewed by CWG.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions