0

What is the bug in the following project?

main.cpp

#include "template_specialization_conflict_test.hpp"

int main()
{
    std::cout << utils::my_template_function(0.555);
    std::cout<<utils::my_template_function<double>(0.555);
    return 0;
}

template_specialization_conflict_test.hpp

#ifndef UTILS__UTILS__UTILS__UTILS
#define UTILS__UTILS__UTILS__UTILS
#include <iostream>

namespace utils
{
    // A generic function
    template <class T>
    T my_template_function(T parameter)
    {
        std::cout << "function template";
        std::cout << parameter;
        return parameter;
    }

    // Template Specialization
    //      A function specialized for double data type
    template <>
    double my_template_function<double>(double parameter)
    {
        std::cout << "function specialization on double";
        std::cout << parameter;
        return parameter;
    }
}
#endif

template_specialization_conflict_test.cpp

#include "template_specialization_conflict_test.hpp"

namespace utils
{
    //empty
}

Error

>------ Rebuild All started: Project: template_specialization_conflict_test, Configuration: x64-Debug ------
  [1/1] Cleaning all built files...
  Cleaning... 2 files.
  [1/3] Building CXX object CMakeFiles\template_specialization_conflict_test.dir\main.cpp.obj
  [2/3] Building CXX object CMakeFiles\template_specialization_conflict_test.dir\template_specialization_conflict_test.cpp.obj
  [3/3] Linking CXX executable template_specialization_conflict_test.exe
  FAILED: template_specialization_conflict_test.exe 
  cmd.exe /C "cd . && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\template_specialization_conflict_test.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\10.0.17763.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\10.0.17763.0\x64\mt.exe --manifests  -- "C:\PROGRA~2\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\link.exe" /nologo CMakeFiles\template_specialization_conflict_test.dir\template_specialization_conflict_test.cpp.obj CMakeFiles\template_specialization_conflict_test.dir\main.cpp.obj  /out:template_specialization_conflict_test.exe /implib:template_specialization_conflict_test.lib /pdb:template_specialization_conflict_test.pdb /version:0.0 /machine:x64 /debug /INCREMENTAL /subsystem:console  kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
  LINK Pass 1: command "C:\PROGRA~2\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\link.exe /nologo CMakeFiles\template_specialization_conflict_test.dir\template_specialization_conflict_test.cpp.obj CMakeFiles\template_specialization_conflict_test.dir\main.cpp.obj /out:template_specialization_conflict_test.exe /implib:template_specialization_conflict_test.lib /pdb:template_specialization_conflict_test.pdb /version:0.0 /machine:x64 /debug /INCREMENTAL /subsystem:console kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:CMakeFiles\template_specialization_conflict_test.dir/intermediate.manifest CMakeFiles\template_specialization_conflict_test.dir/manifest.res" failed (exit code 1169) with the following output:
C:\Users\pc\source\repos\template_specialization_conflict_test\out\build\x64-Debug\main.cpp.obj : error LNK2005: "double __cdecl utils::my_template_function<double>(double)" (??$my_template_function@N@utils@@YANN@Z) already defined in template_specialization_conflict_test.cpp.obj
C:\Users\pc\source\repos\template_specialization_conflict_test\out\build\x64-Debug\template_specialization_conflict_test.exe : fatal error LNK1169: one or more multiply defined symbols found
  ninja: build stopped: subcommand failed.

Rebuild All failed.

How can I fix this?

user366312
  • 16,949
  • 65
  • 235
  • 452
  • Unrelated: `_UTILS_` isn't really allowed. It is reserved for use by the implementation. – ChrisMM Oct 30 '21 at 10:22
  • 2
    Define specializations in a single [*translation unit*](https://en.wikipedia.org/wiki/Translation_unit_(programming)) (source file, essentially). – Some programmer dude Oct 30 '21 at 10:23
  • @Someprogrammerdude, doesn't work either. Plz, be kind enough to show me. – user366312 Oct 30 '21 at 10:27
  • 1
    Move `template <> double my_template_function(double parameter)` and its whole body into the `template_specialization_conflict_test.cpp` source file. – Some programmer dude Oct 30 '21 at 10:30
  • @Someprogrammerdude, doesn't work. – user366312 Oct 30 '21 at 10:34
  • @user366312 shouldn't main.cpp.obj come 1st in the linker list? – πάντα ῥεῖ Oct 30 '21 at 10:37
  • @πάνταῥεῖ, doesn't make any difference. – user366312 Oct 30 '21 at 10:39
  • 1
    Regarding the comment by @ChrisMM, please read [What are the rules about using an underscore in a C++ identifier?](https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) It's the leading underscore followed by an upper-case letter that makes it reserved. – Some programmer dude Oct 30 '21 at 10:41
  • 1
    Did you remember to put the specialization in the `utils` namespace in the source file? Did you remember to ***remove*** the specialization from the header file? If I do that I get no error. – Some programmer dude Oct 30 '21 at 10:44
  • @Someprogrammerdude, then why don't you post an answer? – user366312 Oct 30 '21 at 10:49
  • All function definitions in header files must be inline, otherwise you risk ODR violations as you have observed. – Quimby Oct 30 '21 at 11:04
  • Does this answer your question? [Does it make any sense to use inline keyword with templates?](https://stackoverflow.com/questions/10535667/does-it-make-any-sense-to-use-inline-keyword-with-templates) – Quimby Oct 30 '21 at 11:06
  • @Quimby, Okay, but why moving the definition to the source file solve the issue, then? – user366312 Oct 30 '21 at 11:08
  • @user366312 Because you don't include source files in other source files. The issue is the symbol appearing in multiple Translation Units and the linker does not know what to do. So either you move it into one source file=TU or mark it `inline` so linker does not complain. It is the same as for any other function. Look at [How does the compilation/linking process work?](https://stackoverflow.com/questions/6264249/how-does-the-compilation-linking-process-work). – Quimby Oct 30 '21 at 11:12
  • I'm a little rusty on templates, but why does the specialization for double have any template syntax at all? Why not just declare `double my_template_function(double parameter);`? And then put the definition in template_specialization_conflict_test.cpp? – DS_London Oct 30 '21 at 11:15
  • @DS_London, Coz, I am debugging a larger code-base, and they wrote it like this. I have been debugging this code for the last 5 days. – user366312 Oct 30 '21 at 11:17
  • @DS_London Then it is not a specialization but just a normal function coincidentally named `my_template_function`. Overloading resolution will then have to pick between the template and the function. – Quimby Oct 30 '21 at 11:18
  • @Quimby The resolution seems to work as I would expect it on my compiler (VS 2019). My hazy recollection is that the compiler only generates templated code if it can't find a match among non-templated functions? – DS_London Oct 30 '21 at 11:29
  • @DS_London Overload resolution just picks the correct candidate function, generating the code from templates happens after that if a template has been selected. Yes, non-template functions are preferred but still "the best match" is chosen, that can be a template even if some other candidate exists. Thus between `template foo(T);`, `template<> foo(int);`, and `foo(int)`. The last one would be picked for `foo(5)`. So explicit specializations are still penalized from coming from template functions. `foo(5.0)` picks the generic version even though `foo(int)` would compile too. – Quimby Oct 30 '21 at 11:46
  • @Quimby I don't want to hijack this thread but how is the declaration `template<> foo(int)` resolved? Is this a template specialization or a non-template one? Or indeed `template<> foo<>(int)` which also compiles and runs. – DS_London Oct 30 '21 at 12:03
  • @DS_London The compiler tries to find the primary template that matches the specialization. `<>` after `foo` is not needed as long all the template arguments can be deduced ( by the same algorithm as the one used for calls to template functions). So `template<> foo(int)` is same as writing `template<> foo<>(int)` and the compiler can deduce `T=int` so you do not have to write `template <> foo(int)`. Had you wrote `template<> foo(double)` you would get an error. – Quimby Oct 30 '21 at 12:39

1 Answers1

1

How can I fix this?

You could solve this by adding/using the keyword inline for the specialization so the specialization would look like:

   //note the keyword inline in the below specialization
   template <> inline
    double my_template_function<double>(double parameter)
    {
        std::cout << "function specialization on double";
        std::cout << parameter;
        return parameter;
    }

This works as can be seen here.

Second way to solve this would be to move your specialization into the source file instead of the header. So your template_specialization_conflict_test.cpp would look like: template_specialization_conflict_test.cpp

#include "template_specialization_conflict_test.hpp"

namespace utils
{
    // Template Specialization
    //      A function specialized for double data type
    template <> 
    double my_template_function<double>(double parameter)
    {
        std::cout << "function specialization on double";
        std::cout << parameter;
        return parameter;
    }
}

The program works as can be seen here and here(with gcc and clang).

Jason
  • 36,170
  • 5
  • 26
  • 60
  • What are the other ways? – user366312 Oct 30 '21 at 11:11
  • @user366312 I wrote "one way" because i am taking into account the possibility of other ways(if any). – Jason Oct 30 '21 at 11:13
  • This second way doesn't work in my VS2019. I already tried it. – user366312 Oct 30 '21 at 11:20
  • @user366312 I tried the second method in VS 2019 (both with C++ 14 & 17) and it compiled fine. You need to leave the declaration of the specialization in the header though. The code didn't seem to care whether I included ``, `<>` or neither in the declaration and definition. – DS_London Oct 30 '21 at 11:48
  • @DS_London Definitely need to leave `<>` `` there, as I explained in my comments under the question. Do not mix overload and template specializations if you can avoid it. – Quimby Oct 30 '21 at 11:49
  • @Quimby My comment here crossed with yours above. Thanks for the explanation! – DS_London Oct 30 '21 at 11:55