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-11


2896. Template argument deduction involving exception specifications

Section: 13.10.3  [temp.deduct]     Status: review     Submitter: Krystian Stasiowski     Date: 2024-05-15

(From submission #537.)

Core issue 2355 added support for deducing the exception specification of a function template. However, implementations uniformly reject the following example:

  template<bool B>
  void f() noexcept(B);   // #1

  template<>
  void f() noexcept;      // explicit specialization of #1?

This is arguably the correct approach, because exception specifications are instantiated separately and do not cause substitution failure in the immediate context (13.10.3.1 [temp.deduct.general]). For class-scope explicit specializations, the noexcept-specifier has not even been parsed, yet. If we exclude such deduction from function declarations, we also need to avoid considering the exception specification for partial ordering, otherwise partial ordering would always fail.

A similar consideration applies to the address-of-function-template case:

  struct A { 
    static constexpr bool x = true;
  };

  template<typename T, typename U>
  void f(T, U*) noexcept(T::x);   // #1

  template<typename T, typename U>
  void f(T, U) noexcept(T::y);    // #2

  void(&g)(A, int*) noexcept = f;  // selects #1; not a hard error during deduction for #2

Suggested resolution [SUPERSEDED]:

  1. Change in 13.10.3.7 [temp.deduct.decl] paragraph 1 as follows:

    ... In all these cases, P is the function type of the function template being considered as a potential match and A is either the function type from the declaration or the type of the deallocation function that would match the placement operator new as described in 7.6.2.8 [expr.new] , in all cases ignoring the noexcept-specifier (if any) of the function types. The deduction is done as described in 13.10.3.6 [temp.deduct.type].
  2. Change in 13.10.3.5 [temp.deduct.partial] paragraph 3 as follows:

    The types used to determine the ordering depend on the context in which the partial ordering is done:
    • In the context of a function call, the types used are those function parameter types for which the function call has arguments. [ Footnote: ... ]
    • In the context of a call to a conversion function, the return types of the conversion function templates are used.
    • In other contexts (13.7.7.3 [temp.func.order]), the function template's function type outside of the noexcept-specifier is used.
  3. Change in 13.10.3.3 [temp.deduct.funcaddr] paragraph 1 as follows:

    Template arguments can be deduced from the type specified when taking the address of an overload set (12.3 [over.over]). If there is a target, the function template's function type and the target type are used as the types of P and A, ignoring the noexcept-specifier (if any) of both function types, and the deduction is done as described in 13.10.3.6 [temp.deduct.type]. Otherwise, deduction is performed with empty sets of types P and A.

CWG 2024-06-14

For the last case (address of overload set), the following example is currently well-formed, but is rendered ill-formed by the suggested resolution:

  void f(T*);           // #1
  void f(T) noexcept;   // #2
  void (&g)(int*) noexcept = f;   // status quo: selects #2; suggested resolution: selects #1 (more specialized) and fails the initialization

Possible resolution:

  1. 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. Any function template specialization whose exception specification is originally non-dependent is eliminated from the set unless the function type of the specialization (after possibly applying the function pointer conversion (7.3.14 [conv.fctptr])) is identical to the function type of the target type. 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 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.
  2. Change in 13.10.3.7 [temp.deduct.decl] paragraph 1 as follows:

    ... In all these cases, P is the function type of the function template being considered as a potential match and A is either the function type from the declaration or the type of the deallocation function that would match the placement operator new as described in 7.6.2.8 [expr.new] , in all cases ignoring the noexcept-specifier (if any) of the function types. The deduction is done as described in 13.10.3.6 [temp.deduct.type].
  3. Change in 13.10.3.5 [temp.deduct.partial] paragraph 3 as follows:

    The types used to determine the ordering depend on the context in which the partial ordering is done:
    • In the context of a function call, the types used are those function parameter types for which the function call has arguments. [ Footnote: ... ]
    • In the context of a call to a conversion function, the return types of the conversion function templates are used.
    • In other contexts (13.7.7.3 [temp.func.order]), the function template's function type outside of the noexcept-specifier is used.
  4. Change in 13.10.3.3 [temp.deduct.funcaddr] paragraph 1 as follows:

    Template arguments can be deduced from the type specified when taking the address of an overload set (12.3 [over.over]). If there is a target, the function template's function type and the target type are used as the types of P and A, ignoring the noexcept-specifier (if any) of both function types, and the deduction is done as described in 13.10.3.6 [temp.deduct.type]. Otherwise, deduction is performed with empty sets of types P and A.