This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-10-26


2804. Lookup for determining rewrite targets

Section: 12.2.2.3  [over.match.oper]     Status: open     Submitter: Richard Smith     Date: 2023-10-13     Liaison: EWG

Consider:

  struct X {
    operator int();
    friend bool operator==(X, int);
    friend bool operator!=(X, int);  // #1
  } x;

  bool bx = x == x;    // error: lookup for rewrite target determination does not find hidden friend #1

  struct Y {
    operator int();
    friend bool operator==(Y, int);   // #2
  } y;

  bool operator!=(Y, int);            // #3

  bool by = y == y;                   // OK, #2 is not a rewrite target because lookup finds #3

A similar issue arises for function-scope declarations:

  struct X { operator int(); };

  bool f(X x) {
    bool operator==(X, int);
    return x == x;              // error
  }

  bool g(X x) {
    bool operator==(X, int);
    bool operator!=(X, int);
    return x == x;              // error
  }

  bool operator!=(X, int);

  bool h(X x) {
    bool operator==(X, int);
    return x == x;              // OK
  }

  bool i(X x) {
    bool operator==(X, int);
    bool operator!=(X, int);
    return x == x;              // OK
  }

Are these outcomes as intended? For the bx case at least, probably not.

CWG 2023-10-20

CWG seeks the advice of EWG whether fine-tuning of the "rewrite target" rules is desired, and what exactly those rules should be. See paper issue #1688.

EWG 2023-11-07

The intent is to check for the existence of a declaration that differs only in the operator name (ie, bx should work, by should be ill-formed).

This outcome also resolves issue 2797, but, more importantly, partially reverts P2468R2 (The Equality Operator You Are Looking For), applied in July 2022. In particular, the following example added thereby is no longer well-formed:

  struct B {
    bool operator==(const B&); // #2
  };
  struct C : B {
    C();
    C(B);
    bool operator!=(const B&); // #3
  };
  bool c1 = B() == C(); // OK, calls #2; reversed #2 is not a candidate because search for operator!= in C finds #3

Back to EWG for confirmation.

Additional notes (November, 2023)

P2468R2 also made the following example (derived from real-world code) ambiguous between the reversed built-in candicate and the user-provided operator==:

  template<class T>
  struct Ptr
  {
    bool operator==(T* p) const noexcept { return m_p == p; }
    operator T*() const noexcept { return m_p; }

    T* m_p;
  };

  void foo(Ptr<int> a, Ptr<int> b)
  {
    assert(a == b);
  }

EWG 2024-03-18

EWG invites a paper to propose a change.