Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template Class – Symbols not found [duplicate]

I've seen a few related posts, but I can't really make sense of what I need to do to fix a program I'm making for my entry-level C++ class.

My errors are:

Build Final Project of project Final Project with configuration Debug

Ld "build/Debug/Final Project" normal x86_64
cd "/Users/nick/Dropbox/|Syncs/Xcode/Final Project"
setenv MACOSX_DEPLOYMENT_TARGET 10.6
/Developer/usr/bin/g++-4.2 -arch x86_64 -isysroot /Developer/SDKs/MacOSX10.6.sdk "-        L/Users/nick/Dropbox/|Syncs/Xcode/Final Project/build/Debug" "-F/Users/nick/Dropbox/|Syncs/Xcode/Final Project/build/Debug" -filelist "/Users/nick/Dropbox/|Syncs/Xcode/Final Project/build/Final Project.build/Debug/Final Project.build/Objects-normal/x86_64/Final Project.LinkFileList" -mmacosx-version-min=10.6 -o "/Users/nick/Dropbox/|Syncs/Xcode/Final Project/build/Debug/Final Project"

Undefined symbols:
  "Vector<double>::Vector()", referenced from:
  _main in main.o
  "Vector<double>::length()", referenced from:
  _main in main.o
  "Vector<double>::Vector(double const&, double const&, double const&)", referenced from:
  _main in main.o
  _main in main.o
  "Vector<double>::getx() const", referenced from:
  _main in main.o
  _main in main.o
  "Vector<double>::gety() const", referenced from:
  _main in main.o
  _main in main.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

Here's what I've got:

//main.cpp

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

int main () {
Vector<double> a(1, 2, 3);
Vector<double> b(2, 4, 4);
Vector<double> c;

std::cout << "Length: " << b.length() << std::endl;
std::cout << b.getx() << " ,"  << b.gety() << std::endl;
std::cout << c.getx() << " , " << c.gety() << std::endl;
return 0;
}

and

//Vector.h

template <class T>
class Vector {
T x, y, z;

public:

//constructors
Vector();
Vector(const T& x, const T& y, const T& z);
Vector(const Vector& u);

//accessors
T getx() const;
T gety() const;
T getz() const;

//mutators
void setx(const T& x);
void sety(const T& y);
void setz(const T& z);

//operations
void operator-();
Vector plus(const Vector& v);
Vector minus(const Vector& v);
Vector cross(const Vector& v);
T dot(const Vector& v);
void times(const T& s);
T length();
};

and the Vector.cpp (though I've trimmed some code that's somewhat duplicative – accessors & mutators for y and z, for instance)

//Vector.cpp
#include "Vector.h"
#include <iostream>
#include <math.h>

template class Vector<int>;
template class Vector<double>;

//default constructor
template <class T>
Vector<T>::Vector(): x(0), y(0), z(0) {}


//constructor
template <class T>
Vector<T>::Vector(const T& x, const T& y, const T& z)
{
setx(x);
sety(y);
setz(z);
}

//copy constructor
template <class T>
Vector<T>::Vector(const Vector& u)
{
x = u.getx();
y = u.gety();
z = u.getz();
}

//x accessor
template <class T>
T Vector<T>::getx() const
{
return x;
}

//y accessor

//z accessor

//x mutator
template <class T>
void Vector<T>::setx(const T& x)
{
Vector::x = x;
}

//y mutator

//z mutator

//negated Vector
template <class T>
void Vector<T>::operator-()
{
setx(-this->getx());
sety(-this->gety());
setz(-this->getz());
}

//sum
template <class T>
Vector<T> Vector<T>::plus(const Vector& v)
{
Vector ret((getx() + v.getx()), (gety() + v.gety()), (getz() + v.getz()));
return ret;
}

//difference
template <class T>
Vector<T> Vector<T>::minus(const Vector& v)
{
Vector ret((getx() - v.getx()), (gety() - v.gety()), (getz() - v.getz()));
return ret;
}

//cross product
template <class T>
Vector<T> Vector<T>::cross(const Vector& v)
{
Vector ret;
ret.setx(gety()*v.getz() - getz()*v.gety());
ret.sety(getz()*v.getx() - getx()*v.getz());
ret.setz(getx()*v.gety() - gety()*v.getx());
return ret;

}

//dot product
template <class T>
T Vector<T>::dot(const Vector& v)
{
return (getx()*v.getx() + gety()*v.gety() + getz()*v.getz());   
}

//scalar times Vector
template <class T>
void Vector<T>::times(const T& s)
{
setx(getx()*s);
sety(gety()*s);
setz(getz()*s);
}

//length of Vector
template <class T>
T Vector<T>::length()
{
return sqrt((this->dot(*this)));
}

So, what the hell's going on? Is it because I have a separate header & .cpp file for Vector? How do I get my main function to recognize functions of my Vector class?

like image 411
Nick Sweet Avatar asked Feb 03 '23 04:02

Nick Sweet


2 Answers

Detailed Explanation available from http://www.parashift.com/c++-faq-lite/templates.html

[35.12] Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file?

If all you want to know is how to fix this situation, read the next two FAQs. But in order to understand why things are the way they are, first accept these facts:

  1. A template is not a class or a function. A template is a "pattern" that the compiler uses to generate a family of classes or functions.
  2. In order for the compiler to generate the code, it must see both the template definition (not just declaration) and the specific types/whatever used to "fill in" the template. For example, if you're trying to use a Foo, the compiler must see both the Foo template and the fact that you're trying to make a specific Foo.
  3. Your compiler probably doesn't remember the details of one .cpp file while it is compiling another .cpp file. It could, but most do not and if you are reading this FAQ, it almost definitely does not. BTW this is called the "separate compilation model."

Now based on those facts, here's an example that shows why things are the way they are. Suppose you have a template Foo defined like this:

template<typename T>
class Foo {
 public:
   Foo();
   void someMethod(T x);  
 private:
   T x;
};

Along with similar definitions for the member functions:

 template<typename T>
 Foo<T>::Foo()
 {
   ...
 }

 template<typename T>
 void Foo<T>::someMethod(T x)
 {
   ...
 }

Now suppose you have some code in file Bar.cpp that uses Foo:

// Bar.cpp

void blah_blah_blah()
 {
   ...
   Foo<int> f;
   f.someMethod(5);
   ...
 }

Clearly somebody somewhere is going to have to use the "pattern" for the constructor definition and for the someMethod() definition and instantiate those when T is actually int. But if you had put the definition of the constructor and someMethod() into file Foo.cpp, the compiler would see the template code when it compiled Foo.cpp and it would see Foo when it compiled Bar.cpp, but there would never be a time when it saw both the template code and Foo. So by rule #2 above, it could never generate the code for Foo::someMethod().

A note to the experts: I have obviously made several simplifications above. This was intentional so please don't complain too loudly. If you know the difference between a .cpp file and a compilation unit, the difference between a class template and a template class, and the fact that templates really aren't just glorified macros, then don't complain: this particular question/answer wasn't aimed at you to begin with. I simplified things so newbies would "get it," even if doing so offends some experts.

[35.13] How can I avoid linker errors with my template functions?

Tell your C++ compiler which instantiations to make while it is compiling your template function's .cpp file.

As an example, consider the header file foo.h which contains the following template function declaration:

// File "foo.h"

 template<typename T>
 extern void foo();

Now suppose file foo.cpp actually defines that template function:

// File "foo.cpp"

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

 template<typename T>
 void foo()
 {
   std::cout << "Here I am!\n";
 }

Suppose file main.cpp uses this template function by calling foo():

// File "main.cpp"

 #include "foo.h"

 int main()
 {
   foo<int>();
   ...
 }

If you compile and (try to) link these two .cpp files, most compilers will generate linker errors. There are three solutions for this. The first solution is to physically move the definition of the template function into the .h file, even if it is not an inline function. This solution may (or may not!) cause significant code bloat, meaning your executable size may increase dramatically (or, if your compiler is smart enough, may not; try it and see).

The other solution is to leave the definition of the template function in the .cpp file and simply add the line template void foo(); to that file:

// File "foo.cpp"

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

 template<typename T> void foo()
 {
   std::cout << "Here I am!\n";
 }

 template void foo<int>();

If you can't modify foo.cpp, simply create a new .cpp file such as foo-impl.cpp as follows:

// File "foo-impl.cpp"

 #include "foo.cpp"

 template void foo<int>();

Notice that foo-impl.cpp #includes a .cpp file, not a .h file.

like image 53
Pardeep Avatar answered Feb 05 '23 18:02

Pardeep


The simplest solution here is to include your .cc/.cpp files and have these files include their header files. So for example if we have source.cpp and header.h, and we want to use the source file inside the main.cc file, then add #include "header.h" to source.cpp and #include "source.cpp" to your main.cpp.

Not sure if the compiler will optimize this, but this worked for me.

like image 21
Zorayr Avatar answered Feb 05 '23 19:02

Zorayr