简体   繁体   中英

function template explicit specialization

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.

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: nm 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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM