模板类 - 未找到符号

Nic*_*eet 7 c++ templates symbols

我已经看过一些相关的 帖子,但是我无法理解我需要做些什么来修复我正在为入门级C++课程制作的程序.

我的错误是:

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
Run Code Online (Sandbox Code Playgroud)

这是我得到的:

//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;
}
Run Code Online (Sandbox Code Playgroud)

//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();
};
Run Code Online (Sandbox Code Playgroud)

和Vector.cpp(虽然我修剪了一些有点重复的代码 - 例如y和z的访问器和变换器)

//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)));
}
Run Code Online (Sandbox Code Playgroud)

那么,到底发生了什么?是因为我有一个单独的标题和.cpp文件的Vector?如何让我的主函数识别我的Vector类的功能?

Par*_*eep 11

详细说明可从http://www.parashift.com/c++-faq-lite/templates.html获得

[35.12]为什么我不能将模板类的定义与其声明分开并将其放在.cpp文件中?

如果您只想知道如何解决这种情况,请阅读接下来的两个常见问题解答.但是为了理解为什么事情是这样的,首先接受这些事实:

  1. 模板不是类或函数.模板是编译器用于生成类或函数族的"模式".
  2. 为了让编译器生成代码,它必须同时看到模板定义(不仅仅是声明)和用于"填充"模板的特定类型/任何内容.例如,如果您尝试使用Foo,则编译器必须同时看到Foo模板以及您尝试创建特定Foo的事实.
  3. 您的编译器在编译另一个.cpp文件时可能不记得一个.cpp文件的详细信息.它可以,但大多数不会,如果你正在阅读这个常见问题解答,它几乎肯定不会.BTW这被称为"单独的编译模型".

现在基于这些事实,这里有一个例子,说明为什么事情就是这样.假设您有一个模板Foo定义如下:

template<typename T>
class Foo {
 public:
   Foo();
   void someMethod(T x);  
 private:
   T x;
};
Run Code Online (Sandbox Code Playgroud)

以及成员函数的类似定义:

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

 template<typename T>
 void Foo<T>::someMethod(T x)
 {
   ...
 }
Run Code Online (Sandbox Code Playgroud)

现在假设你在文件Bar.cpp中有一些使用Foo的代码:

// Bar.cpp

void blah_blah_blah()
 {
   ...
   Foo<int> f;
   f.someMethod(5);
   ...
 }
Run Code Online (Sandbox Code Playgroud)

很明显,某个地方的人必须使用"模式"作为构造函数定义和someMethod()定义,并在T实际为int时实例化它们.但是如果你把构造函数和someMethod()的定义放到文件Foo.cpp中,编译器会在编译Foo.cpp时看到模板代码,它会在编译Bar.cpp时看到Foo,但是永远不会它看到模板代码和Foo的时候.因此,通过上面的规则#2,它永远不会生成Foo :: someMethod()的代码.

给专家的一个说明:我显然已经做了几个简化.这是故意的,所以请不要抱怨太大声.如果您知道.cpp文件和编译单元之间的区别,类模板和模板类之间的区别,以及模板实际上不仅仅是美化宏的事实,那么请不要抱怨:这个特定的问题/答案并非一开始就是针对你的.我简化了一些事情,所以新手会"得到它",即使这样做会冒犯一些专家.

[35.13]如何使用模板函数避免链接器错误?

告诉C++编译器在编译模板函数的.cpp文件时要进行哪些实例化.

例如,考虑头文件foo.h,它包含以下模板函数声明:

//文件"foo.h"

 template<typename T>
 extern void foo();
Run Code Online (Sandbox Code Playgroud)

现在假设文件foo.cpp实际定义了该模板函数:

//文件"foo.cpp"

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

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

假设文件main.cpp通过调用foo()使用此模板函数:

//文件"main.cpp"

 #include "foo.h"

 int main()
 {
   foo<int>();
   ...
 }
Run Code Online (Sandbox Code Playgroud)

如果编译并(尝试)链接这两个.cpp文件,大多数编译器都会生成链接器错误.有三种解决方案.第一种解决方案是将模板函数的定义物理移动到.h文件中,即使它不是内联函数.此解决方案可能(或可能不会)导致重大代码膨胀,这意味着您的可执行文件大小可能会急剧增加(或者,如果您的编译器足够智能,可能不会;尝试并查看).

另一种解决方案是将模板函数的定义保留在.cpp文件中,只需添加行模板void foo(); 到那个文件:

//文件"foo.cpp"

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

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

 template void foo<int>();
Run Code Online (Sandbox Code Playgroud)

如果你不能修改foo.cpp,只需创建一个新的.cpp文件,如foo-impl.cpp,如下所示:

//文件"foo-impl.cpp"

 #include "foo.cpp"

 template void foo<int>();
Run Code Online (Sandbox Code Playgroud)

请注意,foo-impl.cpp#包含.cpp文件,而不是.h文件.