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

2024-12-19


2803. Overload resolution for reference binding of similar types

Section: 12.2.4.2.5  [over.ics.ref]     Status: DRWP     Submitter: Brian Bi     Date: 2023-06-14

[Accepted as a DR at the March, 2024 meeting.]

Consider:

  int foo(int*& r);       // #1
  int foo(const int* const& r); // #2

  int *p;
  int x = foo(p);

Both #1 and #2 perform direct reference binding; no qualification conversions are involved. Despite the lack of a rule, implementations prefer #1 over #2.

Proposed resolution (approved by CWG 2023-11-10):

  1. Change in 12.2.4.2.5 [over.ics.ref] paragraph 1 as follows:

    When a parameter of reference type "reference to cv T" binds directly (9.4.4 [dcl.init.ref]) to an argument expression, the implicit conversion sequence is the identity conversion, unless:
    • If the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base conversion (12.2.4.2 [over.best.ics]).
    • Otherwise, if T is a function type, or if the type of the argument is possibly cv-qualified T, or if T is an array type of unknown bound with element type U and the argument has an array type of known bound whose element type is possibly cv-qualified U, the implicit conversion sequence is the identity conversion. [ Note: When T is a function type, the type of the argument may differ only by the presence of noexcept. -- end note]
    • Otherwise, the implicit conversion sequence is a qualification conversion.

    [Example 1: ... —end example]

    If the parameter binds directly to the result of applying a conversion function to the argument expression, the implicit conversion sequence is a user-defined conversion sequence (12.2.4.2.3 [over.ics.user]) whose second standard conversion sequence is either an identity conversion or, if the conversion function returns an entity of a type that is a derived class of the parameter type, a derived-to-base conversion determined by the above rules.
  2. Change in 12.2.4.3 [over.ics.rank] bullet 3.2.5 as follows:

    • S1 and S2 differ only in their qualification conversion (7.3.6 [conv.qual]) and yield similar types T1 and T2, respectively (where a standard conversion sequence that is a reference binding is considered to yield the cv-unqualified referenced type), where T1 can be converted to T2 by a qualification conversion and T2 are not the same type, and const T2 is reference-compatible with T1 (9.4.4 [dcl.init.ref]). [Example 5:
        int f(const volatile int *);
        int f(const int *);
        int i;
        int j = f(&i);  // calls f(const int*)
        int g(const int*);
        int g(const volatile int* const&);
        int* p;
        int k = g(p);          // calls g(const int*)
      
      -- end example] or, if not that,
  3. Change in 12.2.4.3 [over.ics.rank] bullet 3.2.6 as follows:

    • S1 and S2 include reference bindings bind "reference to T1" and "reference to T2", respectively (9.4.4 [dcl.init.ref]), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers where T1 and T2 are not the same type, and T2 is reference-compatible with T1. [Example 6: ...
         int h1(int (&)[]);
         int h1(int (&)[1]);
         int h2(void (&)());
         int h2(void (&)() noexcept);
         void g2() {
           int a[1];
           h1(a);            // calls h1(int (&)[1])
           extern void f2() noexcept;
           h2(f2);            // calls h2(void (&)() noexcept)
         }
      
      -- end example ]