1

In this article, they are saying (c) explicit specialization of (b). My doubt is why can't we say it is explicit specialization of (a) ? because we can specialize the template for any particular type. so while specializing for int*, why they say (c) explicit specialization of (b) .

template<class T>   // (a) a base template 
void f( T );

template<class T>   // (b) a second base template, overloads (a) 
void f( T* );       //     (function templates can't be partially 
                //     specialized; they overload instead)

template<>          // (c) explicit specialization of (b) 
void f<>(int*);

Any comments will be helpful to understand the things.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
Devesh Agrawal
  • 8,982
  • 16
  • 82
  • 131

1 Answers1

3

If (b) didn't exist, then (c) would indeed be a valid specialisation of (a). In fact, just changing the order of the source lines so that (c) appears before the compiler has seen (b) will make it a specialisation of (a)!

Consider the following code:

int main()
{
    int a;
    f(&a);
    return 0;
}

Now put yourself in the compiler's shoes. You need to find a matching function f with an int* argument. What do you do?

  • First, you try all the non-template functions called f that you know about, and see if there are any which match the argument types (in this case, int*).
  • If you can't get a perfect match, you look at all the base templates that you know about. In this case, there are two: f<T> and f<T*>. Note that unlike classes, function templates can't be partially specialised, so as far as the compiler is concerned these are completely separate overloads.
  • Clearly the f<T*> base template is a better match, so you instantiate it with T=int. Now, if you've already seen a specialisation for f<int*>, then you use that, and otherwise you generate the function.

Now here's the funny thing. If we change the order of your original code, to

template<class T> void f( T ); // (i)

template<> void f<>(int*); // (ii)

template<class T> void f( T* ); // (iii)

then the compiler now sees (ii) as a specialisation of (i) -- because it processes things in order, and at the point it reaches (ii) it doesn't know that (iii) exists yet! But since it only matches on base templates, it decides that (iii) is a better match than (i) -- and now (iii) does not have any specialisations, so you get the default instantiation.

It's all pretty confusing, and can trip up even the most experienced C++ programmers at times. So the basic rule is this: don't specialise function templates, but use normal function overloading instead. A regular old, non-template

void f(int*);

would be matched before anything else, and avoids this whole mess.


EDIT: n.m. requested references to the standard in the comments. I'm afraid I only have the C++03 version to hand, but here goes:

Paragraph 4.7.3.3: "A declaration of a function template or class template being explicitly specialized shall be in scope at the point of declaration of an explicit specialization.".

That's why in the above example, (ii) cannot be considered as an explicit specialisation of (iii), because (iii) is not yet in scope.

Section 4.8.3: "When a call to that [function] name is written... template argument deduction (14.8.2) and checking of any explicit template arguments (14.3) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments."

In other words, (as I read it, anyway) it always looks at each base template no matter what -- providing an explicit specialisation makes no difference.

The same paragraph goes on: "For each function template, if the argument deduction and checking succeeds, the template-arguments (deduced and/or explicit) are used to instantiate a single function template specialization which is added to the candidate functions set to be used in overload resolution."

So it's only at this point (as I read it) that explicit specialisations get taken into account.

Lastly, and perhaps most importantly in this case, section 13.3.3 deals with choosing the "best viable function" in the overload set. Two items are relevant:

  • F1 is better than F2 if: "F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.5.2". This is why the f<T*> version gets picked ahead of the f<T> version when trying to match f(int*) -- because it's a "more specialised" template

  • F1 is better than F2 if: "F1 is a non-template function and F2 is a function template specialization", which was the basis for my advice at the end of the original answer.

Phew!

Tristan Brindle
  • 16,281
  • 4
  • 39
  • 82
  • The quotes don't answer the principal question, *why (c) is a specialization of (b) and not of (a)*. Both (a) and (b) are in scope, why (b) is chosen? – n. m. could be an AI Oct 25 '13 at 10:20
  • Ah, sorry, skipped that one as there were far too many references already! From what I can tell it's section 4.5.5.2, "Partial ordering of function templates", which is used when *"an explicit specialization (14.7.3) refers to a function template specialization."* -- in other words, `f` is again "more specialised" (for `int*`) than `f`, so the compiler decides that's the one being explicitly specialised. – Tristan Brindle Oct 25 '13 at 10:25
  • I only have the 1998 std copy on my phone. There 14.7.3/12 seems to imply that (c) isn't even legal. – n. m. could be an AI Oct 25 '13 at 10:35
  • From the 2003 std: 14.7.3/12: *"[ Note: This paragraph is intentionally empty. ]"* :-) – Tristan Brindle Oct 25 '13 at 10:37
  • the article is from 2001 so it would seem that the standard of 1998 applies to it... – n. m. could be an AI Oct 25 '13 at 10:42
  • Well then I'm lost! :-) – Tristan Brindle Oct 25 '13 at 10:45