Search code examples
c++linkerg++

ld symbol not found


Looked around and found a few similar questions but none of them were the same. Most had to do with the constructor or destructor. This issue is, more than likely, a result of my rusty C++ linker memory (picking it back up after a few years).

I'll keep it real simple since this is probably a basic misunderstanding of the linker:

data.h

#pragma once

namespace test {
   class Data_V1 {
   public:
      // some getters/setters
      int getData() { return _d; }
      void setData( int d ) { _d = d; }
   private:
      // some data
      int _d;
   };
}

builder.h

#pragma once

namespace test {
   template <class V>
   class Builder {
   public:
      void build();
   };
}

builder.cpp

#include <iostream>
#include "builder.h"

namespace test {
   template<class V>
   void Builder<V>::build() {
      std::cout << "Insert building logic" << std::endl;
   }
}

main.cpp

#include "builder.h"
#include "data.h"

using namespace test;

int main(int argc, char* argv[]) {
    Builder<Data_V1> b;
    b.build();
}

compiling:

g++ -Wall -ansi -pedantic -c builder.cpp
g++ -Wall -ansi -pedantic -c main.cpp
g++ -Wall -ansi -pedantic -o main main.o builder.o

Link error:

Undefined symbols for architecture x86_64:
   "test::Builder<test::Data_V1>::build()", referenced from: 
       _main in main.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status

Any help would be appreciated!


Solution

  • Template definitions need to be visible to all translation units. Move the definition from the cpp to the header.

    Builder.h

    #pragma once
    
    namespace test {
       template <class V>
       class Builder {
       public:
          void build();
       };
    
       template<class V>
       void Builder<V>::build() {
          std::cout << "Insert building logic" << std::endl;
       }
    }
    

    Before you ask, no, there's no way to hide the implementation unless you know all possible specializations beforehand.

    Templates represent a generalized form for the creation of a new class. If the implementation is not visible, when you attempt to specialize the template, the compiler can't know what code to generate.