Date
2020-12-15.00:00:00
Message id
363

Content

In 13.7.7.3 [temp.func.order], partial ordering is explained in terms of template argument deduction. However, the exact procedure for doing so is not specified. A number of details are missing, they are explained as sub-issues below.

  1. 13.7.7.3 [temp.func.order] paragraph 2 refers to 13.10.3 [temp.deduct] for argument deduction. This is the wrong reference; it explains how explicit arguments are processed (paragraph 2) and how function parameter types are adjusted (paragraph 3). Neither of these steps is meaningful in the context of partial ordering. Next in deduction follows one of the steps in 13.10.3.2 [temp.deduct.call], 13.10.3.3 [temp.deduct.funcaddr], 13.10.3.4 [temp.deduct.conv], or 13.10.3.6 [temp.deduct.type]. The standard does not specify which of these contexts apply to partial ordering.
  2. Because 13.10.3.2 [temp.deduct.call] and 13.10.3.4 [temp.deduct.conv] both start with actual function parameters, it is meaningful to assume that partial ordering uses 13.10.3.6 [temp.deduct.type], which only requires types. With that assumption, the example in 13.7.7.3 [temp.func.order] paragraph 5 becomes incorrect, considering the two templates
        template<class T> void g(T);  // #1
        template<class T> void g(T&); // #2
    
    Here, #2 is at least as specialized as #1: With a synthetic type U, #2 becomes g(U&); argument deduction against #1 succeeds with T=U&. However, #1 is not at least as specialized as #2: Deducing g(U) against g(T&) fails. Therefore, the second template is more specialized than the first, and the call g(x) is not ambiguous.
  3. According to John Spicer, the intent of the partial ordering was that it uses deduction as in a function call (13.10.3.2 [temp.deduct.call]), which is indicated by the mentioning of "exact match" in 13.7.7.3 [temp.func.order] paragraph 4. If that is indeed the intent, it should be specified how values are obtained for the step in 13.10.3.2 [temp.deduct.call] paragraph 1, where the types of the arguments are determined. Also, since 13.10.3.2 [temp.deduct.call] paragraph 2 drops references from the parameter type, symmetrically, references should be dropped from the argument type (which is done in Clause 7 [expr] paragraph 2, for a true function call).
  4. 13.7.7.3 [temp.func.order] paragraph 4 requires an "exact match" for the "deduced parameter types". It is not clear whether this refers to the template parameters, or the parameters of the template function. Considering the example
        template<class S> void g(S);  // #1
        template<class T> void g(T const &); // #3
    
    Here, #3 is clearly at least as specialized as #1. To determine whether #1 is at least as specialized as #3, a unique type U is synthesized, and deduction of g<U>(U) is performed against #3. Following the rules in 13.10.3.2 [temp.deduct.call], deduction succeeds with T=U. Since the template argument is U, and the deduced template parameter is also U, we have an exact match between the template parameters. Even though the conversion from U to U const & is an exact match, it is not clear whether the added qualification should be taken into account, as it is in other places.

Issue 200 covers a related issue, illustrated by the following example:

    template <class T> T f(int);
    template <class T, class U> T f(U);
    void g() {
        f<int>(1);
    }

Even though one template is "obviously" more specialized than the other, deduction fails in both directions because neither function parameter list allows template parameter T to be deduced.

(See also issue 250.)

Nathan Sidwell:

13.7.7.3 [temp.func.order] describes the partial ordering of function templates. Paragraph 5 states,

A template is more specialized than another if, and only if, it is at least as specialized as the other template and that template is not at least as specialized as the first.
To paraphrase, given two templates A & B, if A's template parameters can be deduced by B, but B's cannot be deduced by A, then A is more specialized than B. Deduction is done as if for a function call. In particular, except for conversion operators, the return type is not involved in deduction. This leads to the following templates and use being unordered. (This example is culled from G++ bug report 4672 http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view&pr=4672)
  template <typename T, class U> T checked_cast(U from); //#1
  template <typename T, class U> T checked_cast(U * from); //#2
  class C {};

  void foo (int *arg)
  {
    checked_cast <C const *> (arg);
  }
In the call,

#1 can be deduced with T = 'C const *' and U = 'int *'

#2 can be deduced with T = 'C const *' and U = 'int'

It looks like #2 is more specialized that #1, but 13.7.7.3 [temp.func.order] does not make it so, as neither template can deduce 'T' from the other template's function parameters.

Possible Resolutions:

There are several possible solutions, however through experimentation I have discounted two of them.

Option 1:

When deducing function ordering, if the return type of one of the templates uses a template parameter, then return types should be used for deduction. This, unfortunately, makes existing well formed programs ill formed. For example

  template <class T> class X {};

  template <class T> X<T> Foo (T *);	// #1
  template <class T> int Foo (T const *); // #2

  void Baz (int *p1, int const *p2)
  {
    int j = Foo (p2); //#3
  }
Here, neither #1 nor #2 can deduce the other, as the return types fail to match. Considering only the function parameters gives #2 more specialized than #1, and hence makes the call #3 well formed.

Option 2:

As option 1, but only consider the return type when deducing the template whose return type involves template parameters. This has the same flaw as option 1, and that example is similarly ill formed, as #1's return type 'X<T,0>' fails to match 'int' so #1 cannot deduce #2. In the converse direction, return types are not considered, but the function parameters fail to deduce.

Option 3:

It is observed that the original example is only callable with a template-id-expr to supply a value for the first, undeducible, parameter. If that parameter were deducible it would also appear within at least one of the function parameters. We can alter paragraph 4 of [temp.func.order] to indicate that it is not necessary to deduce the parameters which are provided explicitly, when the call has the form of a template-id-expr. This is a safe extension as it only serves to make ill formed programs well formed. It is also in line with the concept that deduction for function specialization order should proceed in a similar manner to function calling, in that explicitly provided parameter values are taken into consideration.

Suggested resolution:

Insert after the first sentence of paragraph 4 in 13.7.7.3 [temp.func.order]

Should any template parameters remain undeduced, and the function call be of the form of a template-id-expr, those template parameters provided in the template-id-expr may be arbitrarily synthesized prior to determining whether the deduced arguments generate a valid function type.

See also issue 200.

(April 2002) John Spicer and John Wiegley have written a paper on this. See 02-0051/N1393.