4

with the following minimal example i get a linker error on my local system in visual studio 15.8.7 (standard console app with standard settings (just removed precompiled headers)): "Error LNK1179 invalid or corrupt file: duplicate COMDAT '??$f@H@@YAXH@Z'"

#include <cstdio>
template<typename T> void f(T) { printf("1"); }  //#1. T can be deduced
template<typename T> void f(int) { printf("2"); } // #2. T needs to be specified explicitly

int main()
{
    f(8); // a) calls #1
    f<int>(8); // b) calls #2           
}
  • Commenting out either the call a) or call b) will result in successfully linking. Standalone call a) calls template definition #1. The second call b) calls template definition #2. As expected.
  • Building in Release mode succeeds. Output is 11. So both calls call template definition #1. Unexpected. ODR-violoation?
  • Additionally i observed the following strange behaviour (in debug settings):
    1. i comment out template definition #2
    2. i do a full rebuild
    3. i comment back in template definition #2
    4. i build (not rebuild, just build)
    5. build succeeds
    6. output is 11 instead of 12

Incremental linking doing strange things?

On wandbox, godbolt and coliru i can compile,link and run and get the expected behaviour with gcc and clang.

The observations described in bullet point 3 made me think it has to do with incremental linking. But maybe the code is also just not well defined? When studying https://en.cppreference.com/w/cpp/language/function_template i encountered the following:

Two expressions involving template parameters are called functionally equivalent if they are not equivalent, but for any given set of template arguments, the evaluation of the two expressions results in the same value.

and

If a program contains declarations of function templates that are functionally equivalent but not equivalent, the program is ill-formed; no diagnostic is required.

So, is the above code ill formed? Do i have an ODR-violation? Or is everything fine and its just a linker/compiler bug?

edit: fixed point 3. i comment back in definition #2 or course.

Update: New day, problems solved i guess. Today i can not reproduce the problem. I did not change anything on my system. I just booted up, opened my project and it worked like expected. Dont know whats going on. But its better this way compared to learn new special arcane template overloading rules :-P

phön
  • 1,215
  • 8
  • 20

1 Answers1

2

The program is fine. You have two different functions that are being invoked. This is a compiler and/or linker error.

[temp.over.link] leads with:

It is possible to overload function templates so that two different function template specializations have the same type.

[ Example:

// translation unit 1:
template<class T>
  void f(T*);
void g(int* p) {
  f(p); // calls f<int>(int*)
}

// translation unit 2:
template<class T>
  void f(T);
void h(int* p) {
  f(p); // calls f<int*>(int*)
}

— end example ]

Such specializations are distinct functions and do not violate the one-definition rule.

You have two different function templates, full stop. The wording about equivalence has to do with template parameters and dependent expressions ([temp.over.link]/5):

Two expressions involving template parameters are considered equivalent if [...]

int does not involve a template parameter, so in no way can it be considered equivalent to T.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Sounds good. So it is no problem that both template functions in the end get instantiated to its final form of `void f(int)`? (in your given example the template parameter is the same, but not the function parameter. so i just want to be sure). – phön Oct 16 '18 at 15:22
  • I think OP's `f` template functions are two overloads. Overloads of template functions is defined in `[temp.func.order]`. – YSC Oct 16 '18 at 15:24
  • @YSC I don't understand the point of the comment. Yes, they are two overloads. I say that in the answer? – Barry Oct 16 '18 at 15:30
  • @phön The function parameter is the same in the example - both calls invoke a function whose signature is `void(int*)`. – Barry Oct 16 '18 at 15:30
  • A piece is missing ^^. I was asking if you could explain how the compiler chose the right overload, I don't fill like `[temp.func.order]` explains it outside of _template argument deduction_. – YSC Oct 16 '18 at 15:38
  • @YSC Uh, you were not asking anything. But if you would actually like to ask something, you can actually do so directly. – Barry Oct 16 '18 at 15:59