模板类 - 未找到符号

时间:2010-06-09 18:06:13

标签: c++ templates symbols

我见过一些related posts,但我无法理解我需要做些什么来修复我为入门级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

这就是我所拥有的:

//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;
}

//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();
};

和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)));
}

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

4 个答案:

答案 0 :(得分:11)

http://www.parashift.com/c++-faq-lite/templates.html

提供详细说明

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

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

  1. 模板不是类或函数。模板是编译器用于生成类或函数族的“模式”。
  2. 为了让编译器生成代码,它必须同时看到模板定义(不仅仅是声明)和特定类型/用于“填充”模板的任何内容。例如,如果您尝试使用Foo,编译器必须同时看到Foo模板以及您尝试制作特定Foo的事实。
  3. 您的编译器在编译另一个.cpp文件时可能不记得一个.cpp文件的详细信息。它可以,但大多数不会,如果你正在阅读这个常见问题解答,它几乎肯定不会。 BTW这被称为“单独的编译模型。”
  4. 现在基于这些事实,这里有一个例子,说明为什么事情就是这样。假设您有一个模板Foo定义如下:

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

    除了成员函数的类似定义:

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

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

    // Bar.cpp

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

    显然某人某处必须使用构造函数定义的“模式”和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();
    

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

    //文件“foo.cpp”

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

    假设文件main.cpp通过调用foo():

    来使用此模板函数

    //文件“main.cpp”

     #include "foo.h"
    
     int main()
     {
       foo<int>();
       ...
     }
    

    如果编译并(尝试)链接这两个.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>();
    

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

    //文件“foo-impl.cpp”

     #include "foo.cpp"
    
     template void foo<int>();
    

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

答案 1 :(得分:2)

这里最简单的解决方案是包含.cc / .cpp文件,并让这些文件包含其头文件。例如,如果我们有source.cppheader.h,我们想在main.cc文件中使用源文件,那么将#include "header.h"添加到source.cpp和{ {1}} #include "source.cpp"

不确定编译器是否会对此进行优化,但这对我有用。

答案 2 :(得分:1)

  

是因为我有一个单独的标题&amp; Vector的.cpp文件?

  

如何让我的main函数识别Vector类的函数?

将函数模板和类模板成员函数的定义放入头文件中。

实际上,模板的定义需要在main.cpp中可用,以便使用您使用的参数(在这种情况下为double)实例化模板。

答案 3 :(得分:-4)

通过包含cpp而不是标头来解决问题。它的模板和模板没有cpp。创建一个标题并将其余部分存储到* .inl而不是* .cpp。在* .h结束时加入inl。 这样做会。不要#include任何内容。