Title
Enumerating a finite set of built-in candidates
Status
open
Section
12.2.2.3 [over.match.oper]
Submitter
Brian Bi

Created on 2023-07-14.00:00:00 last changed 11 months ago

Messages

Date: 2024-01-07.22:25:55

Suggested resolution:

  1. Add a new paragraph before 12.5 [over.built] paragraph 3 as follows:

    For the purposes of this subclause, a type T is admissible for an operand E if a standard conversion sequence (12.2.4.2.2 [over.ics.scs]) exists from E to T. If E has a class type, then T is also admissible for E if E's class has a non-template conversion function F (11.4.8.3 [class.conv.fct]) that would be viable (12.2.3 [over.match.viable]) for a call of the form (E).N(), where N is a hypothetical id-expression that names F, and a standard conversion sequence to T exists from the type specified by F. If E denotes an overload set (12.3 [over.over]), then T is admissible for E if E contains any non-template function for which T is admissible.

    In the remainder of this subclause, vq represents either volatile or no cv-qualifier.

  2. Change in 12.5 [over.built] paragraph 4 through 6 as follows:

    For every pair (T , vq), where T is a cv-unqualified arithmetic type other than bool or a cv-unqualified pointer to (possibly cv-qualified) object type, there exist candidate operator functions of the form

    vq T& operator++(vq T&);
    T operator++(vq T&, int);
    vq T& operator--(vq T&);
    T operator--(vq T&, int);
    
    if vq T& is admissible for the operand.

    For every (possibly cv-qualified) object type T and for every function type T that has neither cv-qualifiers nor a ref-qualifier, there exist candidate operator functions of the form

    T& operator*(T *);
    if T* is admissible for the operand.

    For every type T such that T* is admissible for the operand, there exist candidate operator functions of the form

    T* operator+(T *);
    

  3. Change in 12.5 [over.built] paragraph 9 as follows:

    For every quintuple (C1 , C2 , T , cv1, cv2 ), where C2 is a class type, C1 is the same type as C2 or is a derived class of C2 , and T is an object type or a function type, there exist candidate operator functions of the form
    cv12 T & operator->*(cv1 C1 *, cv2 T C2 ::*);
    
    where cv12 is the union of cv1 and cv2, if cv2 T C2::* is admissible for the second operand. The return type is shown for exposition only; see 7.6.4 [expr.mptr.oper] for the determination of the operator's result type.
  4. Change in 12.5 [over.built] paragraph 13 through 16 as follows:

    For every cv-qualified or cv-unqualified object type T there exist candidate operator functions of the form

    T* operator+(T *, std::ptrdiff_t);
    T& operator[](T *, std::ptrdiff_t);
    T* operator-(T *, std::ptrdiff_t);
    
    if T is admissible for the first operand.

    For every cv-qualified or cv-unqualified object type T there exist candidate operator functions of the form

    T* operator+(std::ptrdiff_t, T *);
    T& operator[](std::ptrdiff_t, T *);
    
    if T is admissible for the second operand.

    For every T , where T is a pointer to object type and is admissible for the left or right operand, there exist candidate operator functions of the form

    std::ptrdiff_t operator-(T , T );
    

    For every T, where T is an enumeration type, or a pointer type that is admissible for the left or right operand, there exist candidate operator functions of the form

    bool operator==(T , T );
    bool operator!=(T , T );
    bool operator<(T , T);
    bool operator>(T , T );
    bool operator<=(T , T );
    bool operator>=(T , T );
    R operator<=>(T , T );
    
    where R is the result type specified in 7.6.8 [expr.spaceship].

    For every T, where T is a pointer-to-member type and is admissible for the left or right operand, or T is std::nullptr_t, there exist candidate operator functions of the form

    bool operator==(T, T );
    bool operator!=(T , T );
    

  5. Change in 12.5 [over.built] paragraph 19 through 21 as follows:

    For every pair (T , vq), where T is any type, there exist candidate operator functions of the form

    T *vq & operator=(T *vq &, T *);
    
    if T vq& is admissible for the left operand or T* is admissible for the right operand.

    For every pair (T , vq), where T is an enumeration type, or T is a pointer-to-member type such that vq T& is admissible for the left operand or T is admissible for the right operand, there exist candidate operator functions of the form

    vq T & operator=(vq T &, T );
    

    For every pair (T , vq), where T is a cv-qualified or cv-unqualified object type, there exist candidate operator functions of the form

    T *vq &  operator+=(T *vq &, std::ptrdiff_t);
    T *vq &  operator-=(T *vq &, std::ptrdiff_t);
    
    if T*vq& is admissible for the left operand.

  6. Change in 12.5 [over.built] paragraph 25 as follows:

    For every type T, where T is a pointer, pointer-to-member, or scoped enumeration type,
    • T is admissible for the second or third operand and is a pointer or pointer-to-member type, or
    • T is a scoped enumeration type,
    there exist candidate operator functions of the form
    operator?:(bool, T, T);
    
Date: 2023-07-14.00:00:00

(From submission #362.)

Consider the following example, which is accepted by clang, but rejected by gcc:

  #include <concepts>

  struct S1 {
    operator int*() { return nullptr; }
  };

  struct S2 {
    template <class T>
    operator T() requires std::same_as<T, int*> {
      return nullptr;
    }
  };

  int main() {
    S1 s1;
    S2 s2;
    return s1 - s2;
  }

The question is whether the implementation is required to find the built-in candidate std::ptrdiff_t operator-(int*, int*), and select that candidate. Subclause 12.5 [over.built] specifies an infinite number of built-in candidates, such as std::ptrdiff_t operator-(T*, T*) for every object type T. If there are infinitely many built-in candidates, the implementation cannot iterate through all of them to determine whether each candidate is viable.

The rule in 12.2.2.3 [over.match.oper] paragraph 3.3 is insufficient:

  • ...
  • ... For all other operators, the built-in candidates include all of the candidate operator functions defined in 12.5 [over.built] that, compared to the given operator,
    • have the same operator name, and
    • accept the same number of operands, and
    • accept operand types to which the given operand or operands can be converted according to 12.2.4.2 [over.best.ics], and
    • do not have the same parameter-type-list as any non-member candidate or rewritten non-member candidate that is not a function template specialization.
  • ...
History
Date User Action Args
2024-01-07 22:25:55adminsetmessages: + msg7567
2023-07-14 00:00:00admincreate