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

2024-11-19


2918. Consideration of constraints for address of overloaded function

Section: 12.3  [over.over]     Status: ready     Submitter: Richard Smith     Date: 2024-06-26

Consider:

  template<bool B> struct X {
    void f(short) requires B;
    void f(long);
    template<typename> void g(short) requires B;
    template<typename> void g(long);
   };
   void test(X<true> x) {
    x.f(0);           // #1, ambiguous
    x.g<int>(0);      // #2, ambiguous
    &X<true>::f;      // #3, OK!
    &X<true>::g<int>; // #4, ambiguous
  }

For the function call cases, 12.2.4.1 [over.match.best.general] bullet 2.6 and 13.7.7.3 [temp.func.order] paragraph 6 specify that constraints are only considered if the competing overloads are otherwise basically the same. There is no corresponding restriction when taking the address of a function.

For a second issue, the treatment of placeholder type deduction is unclear:

  template<bool B> struct X {
    void f(short) requires B;
    void f(short);
    template<typename> void g(short) requires B;
    template<typename> void g(short);
  };
  void test(X<true> x) {
    auto r = &X<true>::f;       // #5
    auto s = &X<true>::g<int>;  // #6
  }

When the address of the overload set is resolved, there is a target, but the target type is auto, which is not properly handled.

See also issue 2572.

Proposed resolution (September, 2024) [SUPERSEDED]:

  1. Change in 12.2.4.1 [over.match.best.general] bullet 2.6 as follows:

    • F1 and F2 are non-template functions and F1 is more partial-ordering-constrained than F2 (13.5.5 [temp.constr.order])
      • they have the same non-object-parameter-type-lists (9.3.4.6 [dcl.fct]), and
      • if they are member functions, both are direct members of the same class, and
      • if both are non-static member functions, they have the same types for their object parameters, and
      • F1 is more constrained than F2 according to the partial ordering of constraints described in 13.5.5 [temp.constr.order],
      or if not that,
  2. Change in 12.3 [over.over] paragraph 1 as follows:

    ... The target can be If the target type contains a placeholder type, placeholder type deduction is performed (9.2.9.7.2 [dcl.type.auto.deduct]), and the remainder of this subclause uses the target type so deduced.
  3. Change in 12.3 [over.over] paragraph 5 as follows:

    All functions with associated constraints that are not satisfied (13.5.3 [temp.constr.decl]) are eliminated from the set of selected functions. If more than one function in the set remains, all function template specializations in the set are eliminated if the set also contains a function that is not a function template specialization. Any given non-template function F0 is eliminated if the set contains a second non-template function that is more constrained partial-ordering-constrained than F0 according to the partial ordering rules of (13.5.5 [temp.constr.order]). Any given function template specialization F1 is eliminated if the set contains a second function template specialization whose function template is more specialized than the function template of F1 according to the partial ordering rules of 13.7.7.3 [temp.func.order]. After such eliminations, if any, there shall remain exactly one selected function.
  4. Split and change in 12.3 [over.over] paragraph 6 as follows:

    [Example 1: ...

    The initialization of pfe is ill-formed because no f() with type int(...) has been declared, and not because of any ambiguity. For another example, -- end example]

    [Example:

    ...

    -- end example]

    [Example:

      template<bool B> struct X {
        void f(short) requires B;
        void f(long);
        template<typename> void g(short) requires B;
        template<typename> void g(long);
      };
      void test() {
        &X<true>::f;      // error: ambiguous; constraints are not considered
        &X<true>::g<int>; // error: ambiguous; constraints are not considered
      }
    

    -- end example]

  5. Add a paragraph at the end of 13.5.5 [temp.constr.order]:

    A declaration D1 is more constrained than another declaration D2 when D1 is at least as constrained as D2, and D2 is not at least as constrained as D1. [ Example: ... ]

    A non-template function F1 is more partial-ordering-constrained than a non-template function F2 if

    • they have the same non-object-parameter-type-lists (9.3.4.6 [dcl.fct], and
    • if they are member functions, both are direct members of the same class, and
    • if both are non-static member functions, they have the same types for their object parameters, and
    • the declaration of F1 is more constrained than the declaration of F2.

  6. Change in 13.10.3.2 [temp.deduct.call] paragraph 6 as follows:

    When P is a function type, function pointer type, or pointer-to-member-function type:
    • If the argument is an overload set containing one or more function templates, the parameter is treated as a non-deduced context.
    • If the argument is an overload set (not containing function templates), trial argument deduction is attempted using each of the members of the set. If deduction succeeds for only one of the overload set members, that member is used as the argument value for the deduction. If deduction succeeds for more than one member of the overload set If all successful deductions yield the same deduced A, that deduced A is the result of deduction; otherwise, the parameter is treated as a non-deduced context.
  7. Add a new paragraph at the end of 13.10.3.2 [temp.deduct.call] as follows:

    [ Example:
      // All arguments for placeholder type deduction (9.2.9.7.2 [dcl.type.auto.deduct]) yield the same deduced type.
      template<bool B> struct X {
        void f(short) requires B;   // #1
        void f(short);              // #2
      };
      void test() {
        auto x = &X<true>::f;       // OK, deduces void(*)(short), selects #1
        auto y = &X<false>::f;       // OK, deduces void(*)(short), selects #2
      }
    
    -- end example]
  8. Change in 13.10.3.6 [temp.deduct.type] bullet 5.6 as follows:

    • A function parameter for which the associated argument is an overload set (12.3 [over.over]), and one or more of the following apply:
      • more than one function matches functions that do not all have the same function type match the function parameter type (resulting in an ambiguous deduction), or
      • no function matches the function parameter type, or
      • the overload set supplied as an argument contains one or more function templates.
  9. Add a section to C.1.4 [diff.cpp23.temp]:

    Affected subclause: 13.10.3.2 [temp.deduct.call]
    Change: Template argument deduction from overload sets succeeds in more cases.
    Rationale: Allow consideration of constraints to disambiguate overload sets used as parameters in function calls.
    Effect on original feature: Valid C++ 2023 code may become ill-formed.
    [Example 1:

      template <typename T>
      void f(T &&, void (*)(T &&));
    
      void g(int &);
      inline namespace A {
        void g(short &&);
      }
      inline namespace B {
        void g(short &&);
      }
    
      void q() {
        int x;
        f(x, g);         // ill-formed; previously well-formed, deducing T=int&
      }
    
    -- end example]

CWG 2024-09-27

Functions whose constraints are not satisfied should be excluded from the overload set before attempting deduction. Also, clarify that 12.3 [over.over] applies after deduction is complete.

Proposed resolution (approved by CWG 2024-11-19):

  1. Change in 12.2.4.1 [over.match.best.general] bullet 2.6 as follows:

    • F1 and F2 are non-template functions and F1 is more partial-ordering-constrained than F2 (13.5.5 [temp.constr.order])
      • they have the same non-object-parameter-type-lists (9.3.4.6 [dcl.fct]), and
      • if they are member functions, both are direct members of the same class, and
      • if both are non-static member functions, they have the same types for their object parameters, and
      • F1 is more constrained than F2 according to the partial ordering of constraints described in 13.5.5 [temp.constr.order],
      or if not that,
  2. Change in 12.3 [over.over] paragraph 1 as follows:

    ... The target can be If the target type contains a placeholder type, placeholder type deduction is performed (9.2.9.7.2 [dcl.type.auto.deduct]), and the remainder of this subclause uses the target type so deduced.
  3. Change in 12.3 [over.over] paragraph 5 as follows:

    All functions with associated constraints that are not satisfied (13.5.3 [temp.constr.decl]) are eliminated from the set of selected functions. If more than one function in the set remains, all function template specializations in the set are eliminated if the set also contains a function that is not a function template specialization. Any given non-template function F0 is eliminated if the set contains a second non-template function that is more constrained partial-ordering-constrained than F0 according to the partial ordering rules of (13.5.5 [temp.constr.order]). Any given function template specialization F1 is eliminated if the set contains a second function template specialization whose function template is more specialized than the function template of F1 according to the partial ordering rules of 13.7.7.3 [temp.func.order]. After such eliminations, if any, there shall remain exactly one selected function.
  4. Split and change in 12.3 [over.over] paragraph 6 as follows:

    [Example 1: ...

    The initialization of pfe is ill-formed because no f() with type int(...) has been declared, and not because of any ambiguity. For another example, -- end example]

    [Example:

    ...

    -- end example]

    [Example:

      template<bool B> struct X {
        void f(short) requires B;
        void f(long);
        template<typename> void g(short) requires B;
        template<typename> void g(long);
      };
      void test() {
        &X<true>::f;      // error: ambiguous; constraints are not considered
        &X<true>::g<int>; // error: ambiguous; constraints are not considered
      }
    

    -- end example]

  5. Add a paragraph at the end of 13.5.5 [temp.constr.order]:

    A declaration D1 is more constrained than another declaration D2 when D1 is at least as constrained as D2, and D2 is not at least as constrained as D1. [ Example: ... ]

    A non-template function F1 is more partial-ordering-constrained than a non-template function F2 if

    • they have the same non-object-parameter-type-lists (9.3.4.6 [dcl.fct], and
    • if they are member functions, both are direct members of the same class, and
    • if both are non-static member functions, they have the same types for their object parameters, and
    • the declaration of F1 is more constrained than the declaration of F2.

  6. Change in 13.10.3.2 [temp.deduct.call] paragraph 6 as follows:

    When P is a function type, function pointer type, or pointer-to-member-function type:
    • If the argument is an overload set containing one or more function templates, the parameter is treated as a non-deduced context.
    • If the argument is an overload set (not containing function templates), trial argument deduction is attempted using each of the members of the set whose associated constraints (13.5.2 [temp.constr.constr]) are satisfied. If deduction succeeds for only one of the overload set members, that member is used as the argument value for the deduction. If deduction succeeds for more than one member of the overload set If all successful deductions yield the same deduced A, that deduced A is the result of deduction; otherwise, the parameter is treated as a non-deduced context.
  7. Add a new paragraph at the end of 13.10.3.2 [temp.deduct.call] as follows:

    [ Example:
      // All arguments for placeholder type deduction (9.2.9.7.2 [dcl.type.auto.deduct]) yield the same deduced type.
      template<bool B> struct X {
        static void f(short) requires B;   // #1
        static void f(short);              // #2
      };
      void test() {
        auto x = &X<true>::f;       // OK, deduces void(*)(short), selects #1
        auto y = &X<false>::f;       // OK, deduces void(*)(short), selects #2
      }
    
    -- end example]
  8. Change in 13.10.3.6 [temp.deduct.type] bullet 5.6 as follows:

    • A function parameter for which the associated argument is an overload set (12.3 [over.over]), and such that one or more of the following apply:
      • more than one function matches functions whose associated constraints are satisfied and that do not all have the same function type match the function parameter type (resulting in an ambiguous deduction), or
      • no function whose associated constraints are satisfied matches the function parameter type, or
      • the overload set supplied as an argument contains one or more function templates.
      [ Note: A particular function from the overload set is selected (12.3 [over.over]) after template argument deduction has succeeded (13.10.4 [temp.over]). -- end note ]
  9. Add a section to C.1.4 [diff.cpp23.temp]:

    Affected subclause: 13.10.3.2 [temp.deduct.call]
    Change: Template argument deduction from overload sets succeeds in more cases.
    Rationale: Allow consideration of constraints to disambiguate overload sets used as parameters in function calls.
    Effect on original feature: Valid C++ 2023 code may become ill-formed.
    [Example 1:

      template <typename T>
      void f(T &&, void (*)(T &&));
    
      void g(int &);              // #1
      inline namespace A {
        void g(short &&);         // #2
      }
      inline namespace B {
        void g(short &&);         // #3
      }
    
      void q() {
        int x;
        f(x, g);         // ill-formed; previously well-formed, deducing T=int&
      }
    
    There is no change to the applicable deduction rules for the individual g candidates: Type deduction from #1 does not succeed; type deductions from #2 and #3 both succeed. -- end example]