I am not sure as of whether the question is why it works in most compilers or why it fails in VS2008. If the question is the former, we can discuss a simplified version of this:
template <typename T>
void f(T const &) {}
void g(void (*fn)(std::string const&) {}
g(f); // compiles
void (*fn)(double const &) = f;
Pointers to functions are a bit special in the language, since the same name can refer to different overloads. When the name of a function is used in code, the compiler cannot determine which of the overloads is determined by itself, so it will use the target of the expression to determine this. In the case of g(f)
, since the g
function takes a function of type void (std::string const&)
, it will resolve f
to mean f<std::string>
, while in the case of the initialization of fn
the compiler will resolve to the specialization f<double>
.
Note that this is a very commonly used feature of the language:
std::cout << std::endl;
The name std::endl
refers to a template:
template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);
The compiler sees that this is being called on the object std::cout
, of type basic_ostream<char,char_traits<char>>
, and that the only specialization that matches the call to operator<<
is that where charT == char
and traits == char_traits<char>
and picks the correct specialization.