1

I have searched high and low for answers on this but I'm still baffled. As far as I understand from the examples I've found online my code should be valid but it won't compile due to undefined references. I figured out how to get it to compile but I think that's not how one is supposed to do it. So the following is a question I'd very much appreciate answers to.

I'd like to implement a class in C++ with a template member similarly to how std::vector for example can contain data of any type. The following is a minimal example that reproduces the issue.

main.cpp:

#include <iostream>
#include <cstdlib>

#include "Foo.h"

int main(int argc, char** argv) {
  
  Foo<int> foo_int;
  Foo<float> foo_flt;

  return 0;
}

Foo.h:

#ifndef FOO_H
#define FOO_H

template<class T> class Foo {
public:
    Foo();
    Foo(const Foo& orig) = delete;
    virtual ~Foo();
private:
    T* m_f;
};

#endif /* FOO_H */

Foo.cpp

#include <iostream>
#include <typeinfo>
#include "Foo.h"

template<class T> Foo<T>::Foo() {
  std::cout << "Constructing a Foo<" << typeid(T).name() << ">!" << std::endl;
  m_f = new T;
}

template<class T> Foo<T>::~Foo() {
  std::cout << "Destructing a Foo<" << typeid(T).name() << ">!" << std::endl;
}

Now if I try to build the code in NetBeans IDE I get the following output.

cd '/home/pete/Dropbox/NetBeans/Foodinger'
/usr/bin/make -f Makefile CONF=Debug
"/usr/bin/make" -f nbproject/Makefile-Debug.mk QMAKE= SUBPROJECTS= .build-conf
make[1]: Entering directory '/home/pete/Dropbox/NetBeans/Foodinger'
"/usr/bin/make"  -f nbproject/Makefile-Debug.mk dist/Debug/GNU-Linux/foodinger
make[2]: Entering directory '/home/pete/Dropbox/NetBeans/Foodinger'
mkdir -p build/Debug/GNU-Linux
rm -f "build/Debug/GNU-Linux/Foo.o.d"
g++    -c -g -std=c++14 -MMD -MP -MF "build/Debug/GNU-Linux/Foo.o.d" -o build/Debug/GNU-Linux/Foo.o Foo.cpp
mkdir -p build/Debug/GNU-Linux
rm -f "build/Debug/GNU-Linux/main.o.d"
g++    -c -g -std=c++14 -MMD -MP -MF "build/Debug/GNU-Linux/main.o.d" -o build/Debug/GNU-Linux/main.o main.cpp
mkdir -p dist/Debug/GNU-Linux
g++     -o dist/Debug/GNU-Linux/foodinger build/Debug/GNU-Linux/Foo.o build/Debug/GNU-Linux/main.o 
build/Debug/GNU-Linux/main.o: In function `main':
/home/pete/Dropbox/NetBeans/Foodinger/main.cpp:8: undefined reference to `Foo<int>::Foo()'
/home/pete/Dropbox/NetBeans/Foodinger/main.cpp:9: undefined reference to `Foo<float>::Foo()'
/home/pete/Dropbox/NetBeans/Foodinger/main.cpp:9: undefined reference to `Foo<float>::~Foo()'
/home/pete/Dropbox/NetBeans/Foodinger/main.cpp:8: undefined reference to `Foo<int>::~Foo()'
/home/pete/Dropbox/NetBeans/Foodinger/main.cpp:8: undefined reference to `Foo<int>::~Foo()'
collect2: error: ld returned 1 exit status
nbproject/Makefile-Debug.mk:63: recipe for target 'dist/Debug/GNU-Linux/foodinger' failed
make[2]: *** [dist/Debug/GNU-Linux/foodinger] Error 1
make[2]: Leaving directory '/home/pete/Dropbox/NetBeans/Foodinger'
nbproject/Makefile-Debug.mk:60: recipe for target '.build-conf' failed
make[1]: *** [.build-conf] Error 2
make[1]: Leaving directory '/home/pete/Dropbox/NetBeans/Foodinger'
nbproject/Makefile-impl.mk:39: recipe for target '.build-impl' failed
make: *** [.build-impl] Error 2

BUILD FAILED (exit value 2, total time: 760ms)

My understanding is that one should only #include header files and the .cpp files should be given as arguments to the compiler. That's what the build seems to be doing according to its output. If I however add #include "Foo.cpp" to main.cpp the program seems to compile and run just fine. But isn't this how one is not supposed to do it? Or have I found some special case here where I have to #include the .cpp file? Is NetBeans doing something wrong? Is there something wrong with my implementation using templates? At least the code compiles fine if I get rid of them.

Thanks.

  • You must also put the member function definitions in the header file. – CGi03 Oct 18 '22 at 21:33
  • In contrast to non-templated entities, templated entities must (in the usual approach) be defined in the header file. Move the definitions of the member functions from the `.cpp` to the `.h` file. Including a `.cpp` file is a bad way to resolve this. – user17732522 Oct 18 '22 at 21:34
  • Oh, I see. I have managed to miss this piece of information. Thank you. – Petri Hirvonen Oct 18 '22 at 21:38
  • Side note: If you've written a good title (and you have here so kudos!) paste the title into google and see what comes up. In this case the first hit is one of the duplicates. Google plays games with ordering the results based on your past history, but I'd still expect a hit on the first page. The next part is "Do you recognize that the search results are relevant?" and sadly nothing but experience can help you out with that. Still, start by googling your title. – user4581301 Oct 18 '22 at 21:40
  • Thanks for the tip @user4581301. I tried various search terms related to compiling, includes, headers, templates and NetBeans but didn't come to think to try my title. And yes, recognizing and understanding some of the results is occasionally quite difficult, especially since the C++ rabbit hole seems deeep. – Petri Hirvonen Oct 19 '22 at 08:29

0 Answers0