C++ 多文件模板实现

C++ 多文件模板实现,c++,templates,C++,Templates,对于普通函数,声明和定义通常在多个文件中分开,如下所示: // Foo.h namespace Foo { void Bar(); } //Foo.cpp #包括“Foo.h” void Foo::Bar() { cout您需要在.hxx文件中编写模板化方法的定义,并将其包含在声明它的头文件(.hh)的末尾。使用.cc/.cpp定义非模板化方法 因此,您将有一个.hh、一个.hxx(包含在.hh末尾)和一个.cc;因此,包含模板化类头文件也将包含其定义 例如: // list.hh

对于普通函数,声明和定义通常在多个文件中分开,如下所示:

// Foo.h

namespace Foo
{
    void Bar();
}

//Foo.cpp
#包括“Foo.h”
void Foo::Bar()
{

cout您需要在.hxx文件中编写模板化方法的定义,并将其包含在声明它的头文件(.hh)的末尾。使用.cc/.cpp定义非模板化方法

因此,您将有一个.hh、一个.hxx(包含在.hh末尾)和一个.cc;因此,包含模板化类头文件也将包含其定义

例如:

// list.hh
#IFNDEF LIST_HH
# DEFINE LIST_HH

template <typename T>
class List
{
  void fun1(T a);

  void fun2();
}

# include "list.hxx"

#ENDIF

// list.hxx
#IFNDEF LIST_HXX
# DEFINE LIST_HXX

# include "list.hh"

template <typename T>
void List::fun1(T a)
{
   // ...
}

#ENDIF

// list.cc

#include "list.hh"

void List::fun2()
{
   // ...
}


// anywhere.cc

#include "list.hh"

// ...
//list.hh
#IFNDEF列表
#定义列表
模板
班级名单
{
void fun1(ta);
void fun2();
}
#包括“list.hxx”
#恩迪夫
//list.hxx
#IFNDEF列表\u HXX
#定义列表\u HXX
#包括“list.hh”
模板
无效列表::fun1(TA)
{
// ...
}
#恩迪夫
//list.cc
#包括“list.hh”
无效列表::fun2()
{
// ...
}
//anywhere.cc
#包括“list.hh”
// ...
编辑

编译模板有几种策略。最常见的策略是上面描述的让类模板的每个用户实例化代码的策略

但是,由于*.hh文件包含*.hxx文件,因此每次需要模板的简单声明时,都会附带完整的实现。如果实现需要其他声明,如std::string,则强制所有客户端代码解析字符串头

为了避免多次实例化(耗费时间和空间),可以引入第四种类型的文件*.hcc:必须为每个具体模板参数编译一次的文件

EDIT2

直接在头文件中写入模板定义称为包含模型。这样做会增加包含头的成本。这不仅是因为我们添加了模板的定义,还因为我们包含了头(,无论什么)这代表了数千行。对于要编译的重要程序来说是一个真正的问题(我们在这里谈论的是编译的时间)分离模型


我的最后一个论点是:清除头文件,使其仅包含类声明及其文档。这样,任何其他程序员都可以快速读取头文件:这个类的公共接口是什么,文档说明了什么。

模板代码保留在
.hh
文件中。没有理由o使它们成为单独的文件,但最好将定义放在所有声明之后

当编译器生成模板代码时,它会对其进行标记,以便链接器知道一个编译单元(即
.o
文件)中的模板实例化与另一个单元中的模板实例化是完全相同的代码。它将保留一个模板并丢弃其余的模板,而不是使用“乘法定义符号”错误退出


具有良好模板支持的现代编译器也是如此。在GCC的情况下,从2.8开始(我知道,我为GCC 2.7.2.2编写了很多代码,在他们对链接器进行智能化之前,您必须跳转以正确构建模板)我很失望,没有答案提到C++标准<强>允许>将定义与声明分开。
// Foo.h
export template<class T> T f();
//Foo.h
导出模板tf();

//Foo.cpp
#包括“Foo.h”
导出模板tf()
{
//废话
}
不幸的是,几乎没有编译器支持导出


<>但是,在C++ C++中,导出被从C++中删除。

不能把它放在<代码> fo.CPP中,因为它只在创建代码> fo.o>代码>时看到。你可以把它放在<代码> FooSuthnist.H./C>中,它包括“代码> fo.h < /Cord>”,并且被需要模板定义的任何东西所包含,而不是仅仅声明的……所有这些都包括
Foo.h
,因此没有必要将它们分开。好消息是,现代链接器可以识别函数是多个
.o
文件中模板代码的实例化,并且不会因“多定义”而消亡错误,但会选择一个并将所有内容链接到它。我看不出在
.hh
.hxx
文件之间拆分它们有任何意义,因为如果你拉入一个,你总是会同时得到这两个文件。不妨将它们放在一起。为什么方法可以在单独的文件中定义,而独立的函数却不能?编译器不知道确切的原因在编译代码之前,需要实例化它。另外,C++编译器被设计为不知道项目中的其他源文件;这就是为什么我们有“包含文件”的原因。是的。@Mike DeSimone@Maxpm请看我的编辑,这与编译时间有关optimization@Julio:我还是不明白重点。按照你的设置方式,
包括“list.hh”
会让你得到
list.hh
,然后是
list.hxx
,以及
包括“list.hxx”
获取
list.hh
后接
list.hxx
。所有内容都可能在
list.hh
中,您会得到完全相同的结果。我看不出您在编译时优化了什么(通常的“编译时优化”定义),尽管看起来您试图让编译器少编译(即“优化编译时”),但要做到这一点,您需要删除
#include“list.hxx”。C++是什么标准?这几乎是禅宗:如果一个标准允许一些聪明的东西,但是编译器不执行它,这个技术是有效的吗?@迈克:问题是它太复杂了,无法用传统的编译器/链接器工具链来实现,所以所有的主要厂商都禁止它。我们有聪明的编译器和哑链接器,应该是这样。另一方面。请注意,传统的编译器不会检查ODR冲突,但这是标准所禁止的(是的,不需要诊断,诸如此类)。谢谢
// Foo.h
export template<class T> T f();
// Foo.cpp
#include "Foo.h"

export template<class T> T f()
{
    // blah blah
}