C++ template specialization priority. Templated specialization

Viewed 172

The subject looks a bit confusing, however I don't know how to formulate it more properly, sorry =)

Let's look a the following code

#include <iostream>

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

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

template<typename T>
struct S {
  using type = T;
};

template<typename T>
void f(typename S<T>::type value) {
  std::cout << "f<S<T>>" << std::endl;
};

int main() {
  f(123);
  f<int>(123);
}

The output is

$ ./testgcc 
f<int>
f<S<T>>

So the question is why the first call results in f<int> specialization and the second with explicit int template argument results in call to "templated" f<S<int>>()? Is there a rule in standard which states how to instantiate templates in such situations?

Thanks in advance!

PS Tested with different versions of gcc and clang - the behavior is the same. I don't have windows system to test with MSVC, however I tested at godbolt and MSVC results int the following code:

_main   PROC
        ; ....
        push    123                           ; 0000007bH
        call    void f<int>(int)                      ; f<int>
        add     esp, 4
        push    123                           ; 0000007bH
        call    void f<int>(int)                      ; f<int>
        ; ...

So MSVC calls f<int> in both cases. Is this behavior documented as implementation defined?

2 Answers

You have UB here.

Unlike classes, template functions cannot be partially specialized. Template functions can be fully specialized like you wrote

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

But "partial specialization" tends to result in UB as it is treated like unrelated declarations

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

template<typename T>
void f(typename S<T>::type value) {
  std::cout << "f<S<T>>" << std::endl;
};
// These two are conflicting declarations for "f" and compiler has no way to disambiguate.
// Worse due to nature of templates it tends to fail to figure out that there is ambiguity. 
// How to differentiate the two anyways?

Use SFINAE to declare explicitly to which typenames each declaration of f relates to so there are no conflicts.

Let's begin with the simple case: f(123);

In template<typename T> void f(typename S<T>::type), T cannot be deduced.

So only one viable function f<T>(T) with T=int

We choose f<T>(T) (choice happens only with primary template) which resolve in the specialization f<int>(int).

The second case: f<int>(123);

Now, both functions are viable:

  • template<typename T> void f(typename S<T>::type) with T=int
  • f<T>(T) with T=int

The first one is more specialized than the second one, so the first one is chosen.

Note:
Gcc, Clang and Mscv agree Demo.

Related