C++ 为什么模板只能在头文件中实现?
引自: 目前使用模板的唯一可移植方法是使用内联函数在头文件中实现它们 为什么会这样C++ 为什么模板只能在头文件中实现?,c++,templates,undefined-reference,c++-faq,C++,Templates,Undefined Reference,C++ Faq,引自: 目前使用模板的唯一可移植方法是使用内联函数在头文件中实现它们 为什么会这样 (澄清:头文件不是唯一的便携式解决方案。但是它们是最方便的便携式解决方案)。 < P>尽管标准C++没有这样的要求,但有些编译器要求在使用的每个翻译单元中都能使用所有的函数和类模板。实际上,对于这些编译器,模板函数体必须在头文件中可用。重复:这意味着这些编译器不允许在非头文件(如.cpp文件)中定义它们 有一个导出关键字,它应该减轻这个问题,但是它几乎不可移植。 < P>尽管标准C++没有这样的要求,但是一些编译
(澄清:头文件不是唯一的便携式解决方案。但是它们是最方便的便携式解决方案)。
< P>尽管标准C++没有这样的要求,但有些编译器要求在使用的每个翻译单元中都能使用所有的函数和类模板。实际上,对于这些编译器,模板函数体必须在头文件中可用。重复:这意味着这些编译器不允许在非头文件(如.cpp文件)中定义它们有一个导出关键字,它应该减轻这个问题,但是它几乎不可移植。
< P>尽管标准C++没有这样的要求,但是一些编译器要求在使用的每个翻译单元中都能使用所有的函数和类模板。实际上,对于这些编译器,模板函数体必须在头文件中可用。重复:这意味着这些编译器不允许在非头文件(如.cpp文件)中定义它们 有一个export关键字可以缓解这个问题,但它离可移植性还差得远。在将模板编译成目标代码之前,编译器需要实例化模板。只有在模板参数已知的情况下才能实现此实例化。现在想象一个场景,其中模板函数在a.h
中声明,在a.cpp
中定义,并在b.cpp
中使用。编译a.cpp
时,不一定知道即将进行的编译b.cpp
将需要模板的实例,更不用说是哪个特定实例了。对于更多的头文件和源文件,情况可能很快变得更加复杂
有人可能会说,编译器可以更智能地“前瞻”模板的所有用途,但我相信创建递归或其他复杂场景并不困难。顺便说一句,编译器不会这样做。正如Anton所指出的,一些编译器支持模板实例化的显式导出声明,但并非所有编译器都支持它(还?)。在将模板编译成目标代码之前,模板需要由编译器实例化。只有在模板参数已知的情况下才能实现此实例化。现在想象一个场景,其中模板函数在a.h
中声明,在a.cpp
中定义,并在b.cpp
中使用。编译a.cpp
时,不一定知道即将进行的编译b.cpp
将需要模板的实例,更不用说是哪个特定实例了。对于更多的头文件和源文件,情况可能很快变得更加复杂
有人可能会说,编译器可以更智能地“前瞻”模板的所有用途,但我相信创建递归或其他复杂场景并不困难。顺便说一句,编译器不会这样做。正如Anton所指出的,一些编译器支持模板实例化的显式导出声明,但并非所有编译器都支持它(是否?)。注意:没有必要将实现放在头文件中,请参阅本答案末尾的替代解决方案
无论如何,代码失败的原因是,在实例化模板时,编译器使用给定的模板参数创建了一个新类。例如:
template<typename T>
struct Foo
{
T bar;
void doSomething(T param) {/* do stuff using T */}
};
// somewhere in a .cpp
Foo<int> f;
因此,编译器需要访问方法的实现,以使用模板参数(在本例中为
int
)实例化它们。如果这些实现不在头中,它们将无法访问,因此编译器将无法实例化模板
一种常见的解决方案是在头文件中写入模板声明,然后在实现文件(例如.tpp)中实现该类,并在头文件的末尾包含该实现文件
福安
模板
结构Foo
{
无效剂量测定(T参数);
};
#包括“Foo.tpp”
Foo.tpp
template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}
模板
void Foo::doSomething(T参数)
{
//实施
}
这样,实现仍然与声明分离,但编译器可以访问
替代解决方案
另一种解决方案是将实现分开,并显式实例化您需要的所有模板实例:
福安
//没有实现
模板结构Foo{…};
Foo.cpp
// implementation of Foo's methods
// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float
//Foo方法的实现
//显式实例化
模板类Foo;
模板类Foo;
//您只能将Foo与int或float一起使用
如果我的解释不够清楚,您可以查看。警告:没有必要将实现放在头文件中,请参阅本答案末尾的替代解决方案
无论如何,代码失败的原因是,在实例化模板时,编译器使用给定的模板参数创建了一个新类。例如:
template<typename T>
struct Foo
{
T bar;
void doSomething(T param) {/* do stuff using T */}
};
// somewhere in a .cpp
Foo<int> f;
因此,编译器需要访问方法的实现,以使用模板参数(在本例中为
int
)实例化它们。如果这些实现不在头中,它们将无法访问,因此编译器将无法实例化模板
一种常见的解决方案是在头文件中写入模板声明,然后在实现文件(例如.tpp)中实现该类,并在头文件的末尾包含该实现文件
福安
模板
结构Foo
{
无效剂量测定(T参数);
};
#包括“Foo.tpp”
Foo.tpp
template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}
<代码
// implementation of Foo's methods
// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float
template < typename ... >
class MyClass
{
int myMethod()
{
// Not just declaration. Add method implementation here
}
};
template class vector<int>;
#ifndef MyTemplate_h
#define MyTemplate_h 1
template <class T>
class MyTemplate
{
public:
MyTemplate(const T& rt);
void dump();
T t;
};
#endif
#include "MyTemplate.h"
#include <iostream>
template <class T>
MyTemplate<T>::MyTemplate(const T& rt)
: t(rt)
{
}
template <class T>
void MyTemplate<T>::dump()
{
cerr << t << endl;
}
#ifndef MyInstantiatedTemplate_h
#define MyInstantiatedTemplate_h 1
#include "MyTemplate.h"
typedef MyTemplate< int > MyInstantiatedTemplate;
#endif
#include "MyTemplate.cpp"
template class MyTemplate< int >;
#include "MyInstantiatedTemplate.h"
int main()
{
MyInstantiatedTemplate m(100);
m.dump();
return 0;
}
//inner_foo.h
template <typename T>
struct Foo
{
void doSomething(T param);
};
//foo.tpp
#include "inner_foo.h"
template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}
//foo.h
#include <foo.tpp>
//main.cpp
#include <foo.h>
template <class T>
class QueueA {
int size;
...
public:
template <class T> T dequeue() {
// implementation here
}
bool isEmpty();
...
}
// implementation of regular methods goes like this:
template <class T> bool QueueA<T>::isEmpty() {
return this->size == 0;
}
main()
{
QueueA<char> Q;
...
}
#ifndef dynarray_h
#define dynarray_h
#include <iostream>
template <class T>
class DynArray{
int capacity_;
int size_;
T* data;
public:
explicit DynArray(int size = 0, int capacity=2);
DynArray(const DynArray& d1);
~DynArray();
T& operator[]( const int index);
void operator=(const DynArray<T>& d1);
int size();
int capacity();
void clear();
void push_back(int n);
void pop_back();
T& at(const int n);
T& back();
T& front();
};
#include "dynarray.template" // this is how you get the header file
#endif
template <class T>
DynArray<T>::DynArray(int size, int capacity){
if (capacity >= size){
this->size_ = size;
this->capacity_ = capacity;
data = new T[capacity];
}
// for (int i = 0; i < size; ++i) {
// data[i] = 0;
// }
}
template <class T>
DynArray<T>::DynArray(const DynArray& d1){
//clear();
//delete [] data;
std::cout << "copy" << std::endl;
this->size_ = d1.size_;
this->capacity_ = d1.capacity_;
data = new T[capacity()];
for(int i = 0; i < size(); ++i){
data[i] = d1.data[i];
}
}
template <class T>
DynArray<T>::~DynArray(){
delete [] data;
}
template <class T>
T& DynArray<T>::operator[]( const int index){
return at(index);
}
template <class T>
void DynArray<T>::operator=(const DynArray<T>& d1){
if (this->size() > 0) {
clear();
}
std::cout << "assign" << std::endl;
this->size_ = d1.size_;
this->capacity_ = d1.capacity_;
data = new T[capacity()];
for(int i = 0; i < size(); ++i){
data[i] = d1.data[i];
}
//delete [] d1.data;
}
template <class T>
int DynArray<T>::size(){
return size_;
}
template <class T>
int DynArray<T>::capacity(){
return capacity_;
}
template <class T>
void DynArray<T>::clear(){
for( int i = 0; i < size(); ++i){
data[i] = 0;
}
size_ = 0;
capacity_ = 2;
}
template <class T>
void DynArray<T>::push_back(int n){
if (size() >= capacity()) {
std::cout << "grow" << std::endl;
//redo the array
T* copy = new T[capacity_ + 40];
for (int i = 0; i < size(); ++i) {
copy[i] = data[i];
}
delete [] data;
data = new T[ capacity_ * 2];
for (int i = 0; i < capacity() * 2; ++i) {
data[i] = copy[i];
}
delete [] copy;
capacity_ *= 2;
}
data[size()] = n;
++size_;
}
template <class T>
void DynArray<T>::pop_back(){
data[size()-1] = 0;
--size_;
}
template <class T>
T& DynArray<T>::at(const int n){
if (n >= size()) {
throw std::runtime_error("invalid index");
}
return data[n];
}
template <class T>
T& DynArray<T>::back(){
if (size() == 0) {
throw std::runtime_error("vector is empty");
}
return data[size()-1];
}
template <class T>
T& DynArray<T>::front(){
if (size() == 0) {
throw std::runtime_error("vector is empty");
}
return data[0];
}
template <class T>
T min(T const& one, T const& theOther);
#include "Utility.h"
template <class T>
T min(T const& one, T const& other)
{
return one < other ? one : other;
}