0

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?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Jason Umi
  • 31
  • 3
  • 3
    template functions behave as their were marked [`inline`](https://en.cppreference.com/w/cpp/language/inline), so no duplicated symbols. – Jarod42 Feb 26 '21 at 15:01
  • Not the same question, but answers the question: [Conflicting template definitions and ODR](https://stackoverflow.com/questions/46340305/conflicting-template-definitions-and-odr) – t.niese Feb 26 '21 at 15:03
  • A simple answer would be "because it would be awfully impractical", although that's a bit of a weasel tactic: there are lots of C and C++ "features" that would be rightfully called awfully impractical by a large chunk of the userbase yet here we are... – Kuba hasn't forgotten Monica Feb 26 '21 at 18:17

0 Answers0