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+中的不同编译单元中多次编译的模板类+;?_C++_Templates - Fatal编程技术网

C++ 是在C+中的不同编译单元中多次编译的模板类+;?

C++ 是在C+中的不同编译单元中多次编译的模板类+;?,c++,templates,C++,Templates,假设我有以下代码: // templateClass.h #ifndef TEMPLATE_CLASS_H #define TEMPLATE_CLASS_H template <typename T> class tClass { public: tClass(); }; #endif // templateClassDef.inl #ifndef TEMPLATE_CLASS_DEF_INL #define TEMPLATE_CLASS_DEF_INL template

假设我有以下代码:

// templateClass.h
#ifndef TEMPLATE_CLASS_H
#define TEMPLATE_CLASS_H

template <typename T>
class tClass
{
public:
  tClass();
};

#endif

// templateClassDef.inl
#ifndef TEMPLATE_CLASS_DEF_INL
#define TEMPLATE_CLASS_DEF_INL

template <typename T>
tClass<T>::tClass()
{
}

#endif

// normalClass.h
#include "templateClass.h"

class normal
{
public:
  normal();
};

// normalClass.cpp
#include "normalClass.h"
#include "templateClassDef.inl"

normal::normal()
{
  tClass<int> a;
}

// main.cpp
#include "templateClass.h"
#include "templateClassDef.inl"

#include "normalClass.h"

int main()
{
  tClass<int> a;
  normal b;

  return 0;
}
//templateClass.h
#ifndef模板\u类\u H
#定义模板类
模板
类tClass
{
公众:
tClass();
};
#恩迪夫
//templateClassDef.inl
#ifndef模板\u类\u定义\u INL
#定义模板\u类\u定义\u INL
模板
tClass::tClass()
{
}
#恩迪夫
//普通类
#包括“templateClass.h”
普通班
{
公众:
正常();
};
//normalClass.cpp
#包括“normalClass.h”
#包括“templateClassDef.inl”
normal::normal()
{
tClass a;
}
//main.cpp
#包括“templateClass.h”
#包括“templateClassDef.inl”
#包括“normalClass.h”
int main()
{
tClass a;
正常b;
返回0;
}
请注意,
inl
文件不像通常那样包含在头文件中,而是包含在源文件中(我知道这不是标准方式……这只是一个示例)。请注意,
normalcClass.cpp
正在实例化
tClass
main.cpp


我很好奇编译器是否每次遇到显式实例化时都必须从模板类构造实例化,因为它是相同的类型(即
tClass
),即使两个实例化都发生在单独的翻译单元中(
normalClass.cpp
main.cpp
)?另外,编译时间是否会增加(如果前面问题的答案是肯定的,它将再次实例化它,那么这也应该是肯定的)?

一般来说,是的,编译器每次遇到模板类时,通常都会编译它们。

基本上,模板是按编译单元实例化的,这增加了编译时间。在新的C++标准中,有一些扩展和特性来处理这个问题,如显式实例化和外部化。请参阅本文档,了解一些解释和优化技术:


编译器如何实现模板实例化机制取决于编译器。实际上,在翻译单元中使用所需的和可见的函数模板定义时会创建它。这会产生相当多不必要的工作。例如,每次在翻译单元中使用IOstreams时,流和区域设置类中所有对应的函数都会实例化。。。始终使用相同的两种类型


根据模板的用途,可以将实现放入源文件而不是头文件中,并在源文件中显式实例化模板。当有很多不同的实例化被使用时,这是不可行的。在这种情况下,预定义常用的实例化可能是合理的。为此,您可以在C++2011中将实例化声明为extern。当然,您需要在某个地方显式地实例化相应的函数或类。

是的,编译单元彼此独立编译


但是,许多工具链都支持预编译头,从而避免了这种情况,并允许编译器在另一个编译单元中重用已处理的代码。

这取决于编译器,但这两个问题的答案都是肯定的编译器为什么不将新类“存储”在某个地方,以及在遇到新类时,它使用已经编译的类,而不是编译一个新的类?如果它多次创建新类型,为什么不说一些类似于相同的类名和定义的话来出错呢?@Samaursa:请不要引用我的话,但我认为有些编译器实际上会“缓存”模板实例化,以支持高级技术。然而,我认为编译器无论如何都会进行检查,以诊断违反“一个定义”规则的情况。链接器将删除每个实例化的所有副本,但不删除一个副本。但是编译器无法知道您将链接哪些其他模块,因此它必须编译您使用的任何实例,除非您承诺在链接时使用
extern
。与内联函数一样,模板不受多个定义冲突的影响;链接器将只选择一个副本来使用。ODR规则仍然适用,具有相同名称的所有副本必须具有相同的行为,因此链接器选择哪一个并不重要。@Samaursa:问题本质上是“某处”!Iit需要与系统的最新变化保持同步。拥有一个额外的实例化单元,它基本上被任何以某种方式构建的东西所使用,但实际上并不与任何组件相关联,这是维护的噩梦。如果有几百个翻译单元的话,它可以与TinyB项目一起工作,但是如果没有它,高效地构建一套共享数万个翻译单元的项目已经是一场噩梦了!它们针对每个编译单元和每一组模板参数进行编译,这增加了编译时间。不同参数的实例化可能会使二进制文件膨胀。但我从来没有听说过由于多个编译单元,同一个实例化会多次出现在二进制文件中。是的,否则签名会发生冲突。我想指出的是,编译器本身无法知道实际使用的是哪个实例化,因此,所有实例都是按照每个编译单元创建/引用的。我最近遇到了一个共享库的问题,它太大了。静态链接为我做到了这一点。但是“膨胀你的二进制文件”部分是误导性的。PCH似乎没有解决实例化和重用这样的问题。在我正在编写的代码中,这主导了任何解析时间(或PCH)缓解。拥有一个“已编译模板”对象的缓存(例如,相关输入状态和名称的哈希,假设这意味着ODR未被违反),从而可以重用实际的实例化,这将显著提高总体(初始)编译速度。。将问题与PCH分开,解析不会出现瓶颈。这也适用于核心STL的重复实例化(而不是解析)。解析将