10

I've read the Why Not Specialize Function Templates and after experiment a little bit, I found an interesting thing. Here go the main.cxx:

// main.cxx
#include <iostream>

// Declarations
/*
template<class T>
void foo(T);

template<>
void foo(int*);

template<class T>
void foo(T*);
*/

// Definition and specification
template<class T>
void foo(T x)
{
    std::cout << "T version." << std::endl;
}

template<>
void foo(int *i)
{
    std::cout << "int* version." << std::endl;
}

template<class T>
void foo(T *x)
{
    std::cout << "T* version" << std::endl;
}

int main(int argc, char** argv)
{
  int *p;
  foo(p);
}

the interesting thing is: if I leave the declaration part commented, the behaviors are just as the article said, i.e. T* version will be used if definition of int* version goes before its definition and vice and verse. However, if uncomment the declaration block, only int* version will be called no matter which order I use in definitions or declarations. My question is how this declaration come to affect the resolution?

Any ideas? I use g++ 4.2.2 on x86_64-redhat-linux

EDIT: simplify this question after saw AProgrammer's answer

ls.
  • 395
  • 4
  • 13

1 Answers1

10

Distributing the source into three files just confuse the matter: the preprocessing makes one compilation unit and the behavior just depend on the content of the CU and not in how many files it was distributed.

I think that you are surprised that in this case

#include <iostream>

template<class T> void foo(T); // A
template<> void foo(int*); // 1
template<class T> void foo(T*); // B

template<class T> void foo(T x)
{ std::cout << "T version." << std::endl; }

template<> void foo(int *i) // 2
{ std::cout << "int* version." << std::endl; }

template<class T> void foo(T *x)
{ std::cout << "T* version" << std::endl; }

int main(int argc, char** argv) {
  int *p;
  foo(p);
}

you get int* version. This is the expected behavior. While (1) does declare a specialization of template <typename T> void foo(T), (2) isn't the definition of that specialization. (2) defines and declares a specialization of template<class T> void foo(T*); which is then called in main(). This will happen if you gives the three declarations before the three definitions in whatever you put the declarations and definitions. The definition (2) will always see the declaration template<class T> void foo(T*); and thus be a specialization of it.

When a specialization for a function template is declared or defined and it could be a specialization for several function templates (like here (2) can be a specialization of the two overloads A and B, they just need to be declared), it it a specialization of the "more specialized" one. You can see the precise definition of "more specialized" in the standard section 17.5.5.2, but it is quite easy to see that B is a better match than A for (2) and thus (2) is a specialization of (B). (1) declares a specialization of (A) because when (1) is declared, (B) hasn't been seen yet. If you wanted to give the definition of (1) after (B) has been seen, you'd have to write

template <> void foo<int*>(int*) // definition for (1)
{ std::cout << "foo<int*>(int*)\n"; }

You could also be explicit when defining (2):

template<> void foo<int>(int *i) // 2 alternate
{ std::cout << "int* version." << std::endl; }

(but obviously giving (2) and this alternate version in the same CU will gives you an error).

You can be also explicit also when calling the functions:

foo(p); // call (2)
foo<int>(p); // call (2)
foo<int*>(p); // call (1)
AProgrammer
  • 51,233
  • 8
  • 91
  • 143
  • @AProgrammer, (2) also see the declaration of template void foo(T), why wouldn't it be its specification? Is there some hidden rule here? – ls. Jun 09 '11 at 16:36
  • @ls, the rules are hidden under 14.5.5.2 and takes a little less than a page with another page of examples. I don't feel like explaining them here. – AProgrammer Jun 09 '11 at 16:53
  • @AProgrammer, +1 for your answer to confirm my initial assumption i.e. the declaration "give some insight" to the function template resolution. THIS is the mysterious part I want to uncover so I edited my initial question. – ls. Jun 09 '11 at 17:16
  • @AProgrammer got a litte confused by your numbering first:-). I summerize here in case someone like me got confused: 1) is actually a declaration specification of A); 2) is a definition specification of B). So it leaves no definition for 1). In order to add definition for 1), one should use your code block contain "//definition for (1)" . After that, when instantiation, compilier simply behave as the artice said, i.e. choosing the best overload version, which is B) which will choose its specification which is 2). – ls. Jun 10 '11 at 08:34
  • @AProgrammer continued from my last comment... So to prove the article even more, if we add the "//definition for (1)" code block and remove //2, it'll still call "T* version" instead of the fully specification just added. – ls. Jun 10 '11 at 08:44