This question already has answers here:
Why can templates only be implemented in the header file?
(17个答案)
5年前关闭。
我已经看到了related, posts,但是我真的无法理解为入门级C++类编写的程序需要做什么。
我的错误是:
这是我得到的:
和
和Vector.cpp(尽管我修剪了一些重复的代码,例如y和z的访问器和变量)
那么,到底是怎么回事?是因为我为Vector提供了单独的 header 和.cpp文件吗?如何获得主要功能来识别Vector类的功能? 模板不是类或函数。模板是编译器用来生成一系列类或函数的“模式”。 为了使编译器生成代码,它必须同时看到模板定义(不仅是声明)和用于“填充”模板的特定类型/内容。例如,如果您尝试使用Foo,则编译器必须同时看到Foo模板和您尝试制作特定Foo的事实。 您的编译器在编译另一个.cpp文件时可能不记得一个.cpp文件的详细信息。可以,但是大多数人不这样做,如果您正在阅读此常见问题解答,几乎肯定不会。顺便说一句,这称为“单独的编译模型”。
现在,基于这些事实,这里有一个示例,说明了事物之所以如此的原因。假设您具有如下定义的模板Foo:
以及成员函数的类似定义:
现在假设您在文件Bar.cpp中有一些使用Foo的代码:
//Bar.cpp
显然,某处某人将必须使用“模式”作为构造函数定义和someMethod()定义,并在T实际为int时实例化它们。但是,如果将构造函数的定义和someMethod()放入文件Foo.cpp中,则编译器在编译Foo.cpp时会看到模板代码,而在编译Bar.cpp时会看到Foo,但是永远不会看到模板代码和Foo的时间。因此,根据上面的规则2,它永远不会生成Foo::someMethod()的代码。
给专家的说明:上面我显然做了几个简化。这是故意的,所以请不要大声提示。如果您知道.cpp文件和编译单元之间的区别,类模板与模板类之间的区别以及模板实际上不仅仅是美化的宏,那么请不要提示:这个特定问题/答案最初并不是针对您的。我简化了事情,以使新手都能“得到它”,即使这样做会冒犯某些专家。
[35.13]如何避免模板函数出现链接器错误?
告诉您的C++编译器在编译模板函数的.cpp文件时进行哪些实例化。
例如,考虑包含以下模板函数声明的头文件foo.h:
//文件“foo.h”
现在假设文件foo.cpp实际上定义了该模板函数:
//文件“foo.cpp”
假设文件main.cpp通过调用foo()使用此模板函数:
//文件“main.cpp”
如果您编译并(尝试)链接这两个.cpp文件,则大多数编译器都会生成链接器错误。有三种解决方案。第一个解决方案是将模板函数的定义物理上移到.h文件中,即使它不是内联函数也是如此。该解决方案可能(也可能不会!)导致代码大量膨胀,这意味着可执行文件的大小可能会急剧增加(或者,如果编译器足够聪明,则可能不会;请尝试看看)。
另一种解决方案是将模板函数的定义保留在.cpp文件中,而只需将行模板添加为void foo();。到该文件:
//文件“foo.cpp”
如果您不能修改foo.cpp,只需创建一个新的.cpp文件,例如foo-impl.cpp,如下所示:
//文件“foo-impl.cpp”
请注意,foo-impl.cpp#包括一个.cpp文件,而不是.h文件。
(17个答案)
5年前关闭。
我已经看到了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)));
}
那么,到底是怎么回事?是因为我为Vector提供了单独的 header 和.cpp文件吗?如何获得主要功能来识别Vector类的功能?
最佳答案
详细说明可从http://www.parashift.com/c++-faq-lite/templates.html 获得
[35.12]为什么不能将模板类的定义与其声明分开,然后将其放入.cpp文件中?
如果您仅想知道如何解决此问题,请阅读以下两个常见问题解答。但是,为了理解事物为何如此,首先要接受以下事实:
现在,基于这些事实,这里有一个示例,说明了事物之所以如此的原因。假设您具有如下定义的模板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文件。
关于c++ - 模板类–找不到符号,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3008541/