user11923373
user11923373

Reputation: 471

Rule for which function overload to specialize

Consider the code:

#include <iostream>

template <typename T>
void f(T)
{
    std::cout << "Version 1" << std::endl;
}

template <typename T>
void f(T *)
{
    std::cout << "Version 2" << std::endl;
}

template <>
void f<>(int *)
{
    std::cout << "Version 3" << std::endl;
}

int main()
{
    int *p = nullptr;
    f(p);
    return 0;
}

This code will output Version 3. What is happening is that function overloading rules look at the first two versions of void f (the third version is a specialization and does not participate in the overload), and decides that the second version is the better version. Once that decision is made, we then see if any specializations exist for the second version. There is, and we use it.

My question, then, is: How did the compiler know that my explicit specialization was a specialization of the second overload and not the first one? I have not provided it with a template parameter for it to make that choice. Is it simply the case that deciding which function to specialize follows a similar/the same rule as deciding which overload to call (if it were calling the function)? That would make some sense...

Upvotes: 2

Views: 72

Answers (1)

Jarod42
Jarod42

Reputation: 217275

There is that example in template_argument_deduction#Explicit_instantiation

Template argument deduction is used in explicit instantiations, explicit specializations, and those friend declarations where the declarator-id happens to refer to a specialization of a function template (for example, friend ostream& operator<< <> (...)), if not all template arguments are explicitly specified or defaulted, template argument deduction is used to determine which template's specialization is referred to.

P is the type of the function template that is being considered as a potential match, and A is the function type from the declaration. If there are no matches or more than one match (after partial ordering), the function declaration is ill-formed:

template<class X> void f(X a);  // 1st template f
template<class X> void f(X* a); // 2nd template f
template<> void f<>(int* a) { } // explicit specialization of f
// P1 = void(X), A1 = void(int*): deduced X = int*, f<int*>(int*)
// P2 = void(X*), A2 = void(int*): deduced X = int, f<int>(int*)
// f<int*>(int*) and f<int>(int*) are then submitted to partial ordering
// which selects f<int>(int*) as the more specialized template

Upvotes: 2

Related Questions