0

In a library i'm working on, I have something like this template class in a header file:

template <int something>
class Base {
public:
    virtual ~Base();  // Implemented in c++ file
}

class Derived : public Base<100> {
public:
    ~Derived() override = default();
}

The destructor is implemented like so:

template <int something>
Base<something>::~Base() {
    destroyBase();
}

This code is compiled as a static library.

Then, I have this code in a sample:

{
   Derived x;
   x.doSomething();
} // X gets destroyed

I compile the executable and link the static library. This works fine on Ubuntu, but when I try it on mac, the linking fails with

Undefined symbols for architecture x86_64:

  "Base<100>::~Base()", referenced from:

      _main in my_sample.cpp.o

In both cases, I'm using clang and compiling with the exact same cmake settings. All the other symbols defined in the cpp file are defined. This is the only undefined symbol. What is happening?

Rotartsi
  • 527
  • 5
  • 19
  • 4
    Need a [mre] to be sure, but it reads like you've run into [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – user4581301 Jun 15 '20 at 22:30
  • 2
    templates must be implemented in header files (or the definition must somehow be available in all compilation units using it). – Sebastian Hoffmann Jun 15 '20 at 22:30
  • Ah. I understand. Why does this only happen on mac then? Why would it work on ubuntu? – Rotartsi Jun 15 '20 at 22:37
  • In your question, you have not said _where_ you are defining the destructor. Please create a [mcve] to help narrow it down – alter_igel Jun 15 '20 at 22:41

1 Answers1

1

As Sebastian Hoffmann said, templates must be implemented in the header file and not a cpp file, otherwise it won't be visible. That's why I was getting undefined symbols.

the build on mac now works when I implemented everything in the header file. Still, this doesn't explain why this only happens on Mac but works perfectly on ubuntu. Also, Why only the destructor? I had other member functions of Base implemented in a cpp file and they linked just fine!

update: I think the reason only the destructor fails to link is that it is the only one that the client app directly references. The constructor is only referenced through the derived class constructor, and other member functions and operator= and stuff are never actually called. The linker error makes sense from a c++ standpoint, but the difference between Mac and ubuntu is puzzling.

note: I'm using travis ci for builds and this is actually my personal hobby project at https://github.com/RotartsiORG/StoneMason (it's not very good)

Rotartsi
  • 527
  • 5
  • 19
  • 2
    Then this is not really an answer. Why don't you edit the question to specify the exact build commands you are using on the different platforms? – cigien Jun 15 '20 at 22:37
  • 1
    There is potential for an interesting and educational question on why this works on other platforms, so please do update the question. – user4581301 Jun 15 '20 at 22:40
  • Instead of talking about operating systems, focus on compilers. Is this a `clang` vs. `gcc` issue? – tadman Jun 15 '20 at 23:14
  • @tadman I'm building with travis ci and using clang for both (cmake detects clang++ both times too) – Rotartsi Jun 15 '20 at 23:29
  • 1
    Same version of the compiler? Same flags? If so, there shouldn't be differences. – tadman Jun 15 '20 at 23:37
  • Mac: https://travis-ci.com/github/RotartsiORG/StoneMason/jobs/349414141 Ubuntu: https://travis-ci.com/github/RotartsiORG/StoneMason/jobs/349414139 Maybe it's a difference between clang and AppleClang? I'm using the exact same cmake script. – Rotartsi Jun 15 '20 at 23:41