3

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?

vleschuk
  • 81
  • 1
  • 6
  • 1
    This is covered [here](https://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading). The two are *functionally equivalent*, since *"for any given set of template arguments, the evaluation of the two expressions results in the same value"*. Then later: *"If a program contains declarations of function templates that are functionally equivalent but not equivalent, the program is ill-formed; no diagnostic is required."* – Mike Lui Apr 06 '20 at 04:18
  • If `int` shouldn't, then which types should use the S version? And how do you decide? – Jerry Jeremiah Apr 06 '20 at 04:47
  • *"Is this behavior documented as implementation defined?"* No, it is either a bug in compiler(s) or a ill-formed program NDR or UB. – Jarod42 Apr 06 '20 at 09:19

2 Answers2

3

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.

ALX23z
  • 4,456
  • 1
  • 11
  • 18
  • I am interested in how you would use SFINAE? Do you have time to explain? – Jerry Jeremiah Apr 06 '20 at 04:12
  • @JerryJeremiah SFINAE is something simple but it has very very unfriendly syntax. The idea is that you add extra typename that serves as condition which is determined from others - if the typename turns out to be non-compilable nonsense then the declaration is malformed and ignored. You'd better seek online guides about writing SFINEA code - there are both guides and videos on the topic. – ALX23z Apr 06 '20 at 04:22
  • @JerryJeremiah if you meant the exact same case from the post - then it is kinda inherently meaningless. But if you simply want something that is not malformed then just conjure some arbitrary condition `A` on the typename `T` and enable first declaration when `A` is true and enable second declaration when `A` is false. – ALX23z Apr 06 '20 at 04:29
  • *""partial specialization" tends to result in UB"*. Template overloads are fine in general, It is that **aliasing** to `int` which result to 2 `f(int)` which is problematic. – Jarod42 Apr 06 '20 at 06:37
  • @Jarod42 overloading means that you have function with the same name but different input parameters. "partial specialization" means that you have two different definitions for the same template object only that one is more specialized than the other - and thus is preferred. Template classes support it but functions do not. – ALX23z Apr 06 '20 at 07:08
  • Those 2 overload `template get()` and `template get()` have same name, **and** same input parameter (None). OP didn't partial specialize. just add an overload – Jarod42 Apr 06 '20 at 07:32
  • @Jarod42 my bad, in template case overloading isn't just input parameters but also template parameters. `get()` are distinguishable by template parameters, his `f` function declaration isn't distinguishable by either template or input parameters. Technically, what he has is a redefinition. From his post I understood that he is interested in template specialization - not overloading which is much more basic. – ALX23z Apr 06 '20 at 07:48
  • It is not re-definition, (it is not even UB/ill-formed IMO). See my answer. – Jarod42 Apr 06 '20 at 08:04
  • @Jarod42 template functions do not support partial specialization. – ALX23z Apr 06 '20 at 08:22
  • I never tell that template function support partial specialization. – Jarod42 Apr 06 '20 at 08:42
  • @Jatod42 "The first one is more specialized than the second one, so the first one is chosen." Then what is that? The choice and result of compilation will change in your demo if you simply rearrange the declarations. Furthermore, OP's code does not link on MSVC. – ALX23z Apr 06 '20 at 08:55
  • [Overload resolution 5.](https://en.cppreference.com/w/cpp/language/overload_resolution#Best_viable_function) `template f(std::vector)` is more specialized than (unrelated) `template f(T)`. There are no partial specialization (as not possible with function), but one is more specialized than the other. – Jarod42 Apr 06 '20 at 09:05
  • @Jarod42 these two are distinct by template/input parameters. In the OP case you have `template f(T)` and `template f(...)` and the `...` results into `T` which is why this is a redefinition or partial specialization at best. – ALX23z Apr 06 '20 at 09:37
  • It is "generally" (but indeed not that way) used by SFINAE [Demo](https://coliru.stacked-crooked.com/a/22227a6cff9f5f1d). functions are not "equivalent" because after substitution, they would result is same signature. – Jarod42 Apr 06 '20 at 09:49
0

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.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • They do not agree. If you simply switch order of declarations result will change. It is simply UB. – ALX23z Apr 06 '20 at 08:31
  • Other order gives same result [here](https://godbolt.org/z/bxbXRA). consexpr should avoid UB, remains only ill formed program NDR. – Jarod42 Apr 06 '20 at 08:55
  • Please, learn to check your own claims https://godbolt.org/z/-ppupY – ALX23z Apr 06 '20 at 09:34
  • @ALX23z: You cannot write (full) specialization before the generic template. Your version doesn't compile (not because of the `static_assert`). – Jarod42 Apr 06 '20 at 09:39
  • it is not working on MSVC as it creates linker errors. Both of these functions generate the same symbols resulting in UB. Furthermore, if `f(123)` results in one number but `f(123)` results in another number is already a huge problem. In, VS2019 if both functions are used then they both return 0, but if only one is present then they result in either 0 or 3. – ALX23z Apr 06 '20 at 10:05
  • Could you provide code link where you have different behavior? Even if strange, `f(123)` might (and do) result in different numbers, and it is not a problem. – Jarod42 Apr 06 '20 at 11:39
  • Take a look at MSVC output; I think the only reason why gcc and clang are somehow able to work with this mess is because they generate separate link symbol for `template T f(T);` for the template parameter deduction - they export it as `T f(T);` for some reason. – ALX23z Apr 06 '20 at 15:26
  • Ok, indeed, there is an issue, but for me, it is a msvc bug. which names 2 different entities the same way. Especially when adding `constexpr` resolve the issue. – Jarod42 Apr 06 '20 at 15:34
  • this isn't a MSVC bug. Both of these declarationd should generate symbol `T f(T);` which is an ODR violation - why gcc and clang generete `T f(T);` is beyond me. Question: how fo you even make a function pointers towards them? – ALX23z Apr 06 '20 at 15:45
  • I already asked similar question ^_^ [overload-resolution-of-template-function-with-auto](https://stackoverflow.com/questions/41175957/overload-resolution-of-template-function-with-auto) – Jarod42 Apr 06 '20 at 16:03