3

I'm having difficulty understanding the how formal ordering rules work as described in chapter 12 of the book C++ Templates, The Complete Guide by D. Vandevoorde and N. M. Josuttis. On page 188 of this book the authors give the following scenario used to decide which of two viable function templates is more specialized:

From these two templates we synthesize two lists of argument types by replacing the template parameters as described earlier: (A1) and (A2*) (where A1 and A2 are unique made up types). Clearly, deduction of the first template against the second list of argument types succeeds by substituting A2* for T. However, there is no way to make T* of the second template match the nonpointer type A1 in the first list. Hence, we formally conclude that the second template is more specialized than the first.

I'd like some understanding this example.

Edit

I believe that the two function templates referred to in the above quote are

template<typename T>
int f(T)
{
    return 1;
}

template<typename T>
int f(T*)
{
    return 2;
}
Olumide
  • 5,397
  • 10
  • 55
  • 104
  • I don't understand why `T` is a non-pointer type in the first one. Why couldn't `T` be `int*`? – Seth Carnegie Apr 03 '12 at 12:02
  • `T` could be `int*` in the first one, but the second will get chosen since, with `T = int`, it is more specialized. Basically, the first one only gets chosen for non-pointer `T`. – Mike DeSimone Apr 03 '12 at 12:34
  • 1
    @SethCarnegie: When a template is more specialized than other, there is a subset of the universe of types that matches both templates. Of course you can find elements in that subset, but the important part is not that, but the fact that there are types that fall outside of the set of valid instantiations of the more specialized template that are valid instantiations of the more generic one, together with the fact that all valid instantiations of the specialized are also valid instantiations of the more general (or else there would be no defined order) – David Rodríguez - dribeas Apr 03 '12 at 12:46
  • @DavidRodríguez-dribeas ah I see, that's a good way to think of it. – Seth Carnegie Apr 04 '12 at 03:15

2 Answers2

5

The rules are a bit harder to explain than to use. The idea is that a template is more specialized than another template if the set of possible instantiations of the more specialized is a strict subset of the set of possible instantiations of the less specialized one.

That is, every type that can be use as an argument to the more specialized can also be used as argument to the less specialized, and there is at least one type that can be used with the less specialized that cannot be used with the more specialized.

Given the two templates:

template <typename A> void f( A );   // [1]
template <typename B> void f( B* );  // [2]

The question to resolve is which one of them is more generic (i.e. can take a greater number of arguments). The whole description in the standard is done in terms of synthetic unique types that are used for A and B, but in a less precise way we can try to resolve by hand waving.

Say we find a type X that matches the second template argument, then an instantiation of that second template will look like void f( X* ) (besides the fact that it is a template). Now, can the template [1] be used to generate an equivalent function? Yes, by making A == X* in the type deduction. Can we do it in the opposite direction? Say we find a type Y with which we can instantiate the first template, we get void f( Y ). Can the second template match this call? No, only for the subset of types that are pointers the previous statement can hold.

That means that the second template is more specialized, since for every valid instantiation of the second template, we can also instantiate the first template, but there are some instantiations of the first template that would not be valid instantiations of the second.

For a practical example, f( char* ) can be matched by both templates, but f( 5 ) can only be matched by the first one. The reason for the weird explanation in terms of synthetic types is that a single example does not guarantee the order, it has to hold for all types. The synthetic type is a representative of any type.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Thanks. Pardon my slowness but I'm still a bit unsure why the second template cannot match `void f( Y )` instantiated by the first template. – Olumide Apr 03 '12 at 13:51
  • @Olumide: it can *if and only if* `Y` represents a pointer to a type. You need to view `Y` as in "for all `Y` in Types", not as a pre-determined type, and from there it is similar to maths. – Matthieu M. Apr 03 '12 at 14:28
  • @Olumide: It is not enough that for a particular type `Y` (as in `int*`) you can do it, the question is whether there is a single type `Y` (say `int`) where you cannot. That is, `f( 5 )` can be matched by the first template with `A == int` but cannot be matched by the second template (*there is no type `Y` such that `Y*` is `int`*) – David Rodríguez - dribeas Apr 03 '12 at 14:43
0

I think the point of the authors is that T in the first template can be matched to both a type A and a pointer to a type A*, while the second template can only be matched to a pointer to a type A*, therefore the second template is more specialized.

Lubo Antonov
  • 2,301
  • 14
  • 18