Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么模板只能在头文件中实现?_C++_Templates_Undefined Reference_C++ Faq - Fatal编程技术网

C++ 为什么模板只能在头文件中实现?

C++ 为什么模板只能在头文件中实现?,c++,templates,undefined-reference,c++-faq,C++,Templates,Undefined Reference,C++ Faq,引自: 目前使用模板的唯一可移植方法是使用内联函数在头文件中实现它们 为什么会这样 (澄清:头文件不是唯一的便携式解决方案。但是它们是最方便的便携式解决方案)。 < P>尽管标准C++没有这样的要求,但有些编译器要求在使用的每个翻译单元中都能使用所有的函数和类模板。实际上,对于这些编译器,模板函数体必须在头文件中可用。重复:这意味着这些编译器不允许在非头文件(如.cpp文件)中定义它们 有一个export关键字可以缓解这个问题,但它离可移植性还差得远。在将模板编译成目标代码之前,编译器需要实例化

引自:

目前使用模板的唯一可移植方法是使用内联函数在头文件中实现它们

为什么会这样


(澄清:头文件不是唯一的便携式解决方案。但是它们是最方便的便携式解决方案)。

< P>尽管标准C++没有这样的要求,但有些编译器要求在使用的每个翻译单元中都能使用所有的函数和类模板。实际上,对于这些编译器,模板函数体必须在头文件中可用。重复:这意味着这些编译器不允许在非头文件(如.cpp文件)中定义它们

有一个export关键字可以缓解这个问题,但它离可移植性还差得远。

在将模板编译成目标代码之前,编译器需要实例化模板。只有在模板参数已知的情况下才能实现此实例化。现在想象一个场景,其中模板函数在
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 ... >
class MyClass
{

    int myMethod()
    {
       // Not just declaration. Add method implementation here
    }
};
模板
类MyClass
{
int myMethod()
{
//不仅仅是声明。在这里添加方法实现
}
};

实际上,在C++11之前,标准定义了
export
关键字,这使得在头文件中声明模板并在其他地方实现它们成为可能

没有一个流行的编译器实现了这个关键字。我所知道的唯一一个是由爱迪生设计小组编写的前端,它由COMUE C++编译器使用。所有其他人都要求您在头文件中编写模板,因为编译器需要模板定义来正确实例化(正如其他人已经指出的)


因此,ISO C++标准委员会决定删除C++中的“代码>出口导出/代码>特性。< < /P> < p>这里有大量正确的答案,但我想补充(完整):

如果在实现cpp文件的底部,对模板将使用的所有类型进行显式实例化,链接器将能够像往常一样找到它们


编辑:添加显式模板实例化的示例。在定义模板和所有成员函数后使用

template class vector<int>;
模板类向量;
这将实例化(从而使链接器可用)类及其所有成员函数(仅限)。类似的语法适用于模板函数,因此,如果有非成员运算符重载,则可能需要对这些函数执行相同的操作


上面的示例是相当无用的,因为向量是在头中完全定义的,除非公共包含文件(预编译头?)使用
extern模板类向量
,以防止它在使用向量的所有其他(1000?)文件中实例化

这是完全正确的,因为编译器必须知道分配的类型。所以模板类、函数、枚举等等。。如果要将其公开或作为库(静态或动态)的一部分,则必须在头文件中也实现,因为头文件的编译与c/cpp文件不同,c/cpp文件是静态的或动态的。如果编译器不知道类型为,则无法编译它。在.Net中,由于所有对象都派生自对象类,因此它可以。这不是.Net。

这是因为需要单独编译,而且模板是insta
// 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;
}