I would like to know why a template instantiated function in two independent object files does not result in a symbol duplication when linking together. I am running a clang version 11.0.0 compiler on MacOS.
For example, I have a template function defined in a C++ header file, template.h
, like this:
#include <iostream>
template<typename T>
void print(T msg)
{
std::cout << "From Template print:" << msg << "\n";
}
Then I have two source files, a.cpp
and b.cpp
, include this header file, like:
a.cpp:
#include "template.h"
#include <string>
void a_func(const std::string& s)
{
print("a::" + s);
return;
}
b.cpp:
#include "template.h"
#include <string>
void b_func(const std::string& s)
{
print("b::" + s);
return;
}
Then I compile both cpp files independently, like:
c++ -o a.o -c a.cpp
c++ -o b.o -c b.cpp
My understanding of the template instantiation is, since the a_func()
called the print<>()
with a string
type argument, the compiler generated a function with a signature like void print(string)
, in the object file a.o
. Then the compiler did the same thing to the object file b.o
. So the same symbol void print(string)
was defined in both a.o
and b.o
.
So now I have a main program to bring both object files together, like:
#include <string>
extern void a_func(const std::string& s);
extern void b_func(const std::string& s);
int main(int argc, char *argv[])
{
a_func("Call from MAIN to A");
b_func("Call from MAIN to B");
return 0;
}
Then compile and link them together:
c++ -o main.o -c main.cpp
c++ -o main main.o a.o b.o
I expected it won't compile, but it did. I thought the same symbol, void print(string)
was defined twice in both a.o
and b.o
. When they were linked with main.o
, the linker would see the duplicated symbol and complain. However this is not the case, so my understanding must be incorrect. I like to know why the linker does not treat the template instantiated functions in two object files as duplicated.
The objdump
showed both object files had the same void print(string)
symbol:
$ objdump -t a.o |grep print
0000000000000080 gw F __TEXT,__text __Z5printINSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEEEvT_
$ objdump -t b.o |grep print
0000000000000080 gw F __TEXT,__text __Z5printINSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEEEvT_
If I manually define a function in the template.h
, and include it to both a.cpp
and b.cpp
, like:
#include <iostream>
template<typename T>
void print(T msg)
{
std::cout << "From Template print:" << msg << "\n";
}
void samefunc(void)
{
return;
}
The linker complained the duplicated symbol as expected:
c++ -o main.o -c main.cpp
c++ -o a.o -c a.cpp
c++ -o b.o -c b.cpp
c++ -o main main.o a.o b.o
duplicate symbol 'samefunc()' in:
a.o
b.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Why it is different from the template instantiated version?