Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

resolution of c++template specification and overload

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

like image 444
ls. Avatar asked Jun 09 '11 15:06

ls.


People also ask

What is overload resolution?

The process of selecting the most appropriate overloaded function or operator is called overload resolution. Suppose that f is an overloaded function name. When you call the overloaded function f() , the compiler creates a set of candidate functions.

Can you overload a template?

A template function can be overloaded either by a non-template function or using an ordinary function template.

What does overload resolution failed mean?

It generates this error message when one overload is more specific for one argument's data type while another overload is more specific for another argument's data type.

What is an overloaded function C++?

C++ lets you specify more than one function of the same name in the same scope. These functions are called overloaded functions, or overloads. Overloaded functions enable you to supply different semantics for a function, depending on the types and number of its arguments.


1 Answers

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)
like image 153
AProgrammer Avatar answered Nov 14 '22 17:11

AProgrammer