为什么仅在标题文件中实现模板?

发布于 2025-01-21 17:20:59 字数 918 浏览 5 评论 0 原文

引用来自 c ++标准库:教程和手册::

目前使用模板的唯一便携方式是通过使用内联函数在标题文件中实现它们。

为什么这是?

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

Quote from The C++ standard library: a tutorial and handbook:

The only portable way of using templates at the moment is to implement them in header files by using inline functions.

Why is this?

(Clarification: header files are not the only portable solution. But they are the most convenient portable solution.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(20

梦幻的心爱 2025-01-28 17:20:59

警告:将实现放入标题文件中是不是,请参见本答案末尾的替代解决方案。

无论如何,您的代码失败的原因是,在实例化模板时,编译器会使用给定的模板参数创建新类。例如:

template<typename T>
struct Foo
{
    T bar;
    void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo<int> f; 

阅读此行时,编译器将创建一个新类(我们称其为 fooint ),这等同于以下内容:

struct FooInt
{
    int bar;
    void doSomething(int param) {/* do stuff using int */}
};

因此,编译器需要访问方法的实现。 ,用模板参数实例化(在这种情况下 int )。如果这些实现不在标题中,则将无法访问它们,因此编译器将无法实例化模板。

一个常见的解决方案是在标题文件中写入模板声明,然后在实现文件中实现类(例如.tpp),然后在标题末尾包含此实现文件。

foo.h

template <typename T>
struct Foo
{
    void doSomething(T param);
};

#include "Foo.tpp"

foo.tpp

template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}

以这种方式,实施仍然与声明分开,但编译器可以访问。

另一种解决

方案是将实现分开,并明确实例化所需的所有模板实例:

foo.h

// no implementation
template <typename T> struct 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

如果我的解释不够清楚,您可以查看 c ++ super-faq在此主题上

Caveat: It is not necessary to put the implementation in the header file, see the alternative solution at the end of this answer.

Anyway, the reason your code is failing is that, when instantiating a template, the compiler creates a new class with the given template argument. For example:

template<typename T>
struct Foo
{
    T bar;
    void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo<int> f; 

When reading this line, the compiler will create a new class (let's call it FooInt), which is equivalent to the following:

struct FooInt
{
    int bar;
    void doSomething(int param) {/* do stuff using int */}
};

Consequently, the compiler needs to have access to the implementation of the methods, to instantiate them with the template argument (in this case int). If these implementations were not in the header, they wouldn't be accessible, and therefore the compiler wouldn't be able to instantiate the template.

A common solution to this is to write the template declaration in a header file, then implement the class in an implementation file (for example .tpp), and include this implementation file at the end of the header.

Foo.h

template <typename T>
struct Foo
{
    void doSomething(T param);
};

#include "Foo.tpp"

Foo.tpp

template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}

This way, implementation is still separated from declaration, but is accessible to the compiler.

Alternative solution

Another solution is to keep the implementation separated, and explicitly instantiate all the template instances you'll need:

Foo.h

// no implementation
template <typename T> struct 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

If my explanation isn't clear enough, you can have a look at the C++ Super-FAQ on this subject.

尤怨 2025-01-28 17:20:59

这是因为需要单独的汇编和模板是实例化式多态性。

让我们更接近混凝土以进行解释。说我有以下文件:

  • foo.h
    • 声明类myclass&lt; t&gt;
    • 的接口

  • 声明类myclass&lt; t&gt; foo.cpp
    • 定义类myclass&lt; t&gt;
    • 的实现

  • 定义类myclass&lt; t&gt; bar.cpp
    • 使用 myclass&lt; int

单独的汇编意味着我应该能够独立于 bar.cpp 独立于 foo.cpp 。编译器在每个编译单元上完全独立地完成了分析,优化和代码生成的所有艰苦工作;我们不需要进行全程分析。只有链接器才需要一次处理整个程序,并且链接器的作业非常容易。

bar.cpp 当我编译 foo.cpp 时,甚至不需要存在,但是我仍然应该能够链接 foo.o.o 我已经和 bar.o 我只是刚刚制作的,而无需重新编译 foo.cpp foo.cpp 甚至可以将其编译到动态库中,分布在没有 foo.cpp 的其他地方,并与我写 foo.cpp后写几年的代码联系

“实例化式多态性”是指模板 myclass&lt; t&gt; 并不是真正可以将可以编译到代码的通用类,该代码可以适用于任何值的> t 。这将添加诸如拳击之类的开销,需要将功能指针传递给分配器和构造函数等。C++模板的意图是避免编写几乎相同的 class myclass_int class myclass_float 等,但仍然能够最终得到编译的代码,就像我们 分别编写了每个版本一样。因此,模板是实际上模板;类模板是不是类,它是为我们遇到的每个 t 创建新类的食谱。不能将模板编译到代码中,只能编译实例化模板的结果。

因此,当编译 foo.cpp 时,编译器看不到 bar.cpp 知道需要 myclass&lt; int&gt; 。它可以看到模板 myClass&lt; t&gt; ,但它不能为此发射代码(这是模板,而不是类)。当编译 bar.cpp 时,编译器可以看到它需要创建一个 myClass&lt; int&gt; ,但是它看不到模板 myClass&lt; t&gt; (仅其接口 foo.h ),因此无法创建它。

如果 foo.cpp 本身使用 myclass&lt; int ,则将在编译 foo.cpp 时生成代码,因此,当时Bar.o foo.o 相关联,它们可以连接并可以工作。我们可以使用该事实来通过编写单个模板在.cpp文件中实现有限的模板实例。但是, bar.cpp 无法将模板用作模板并在其喜欢的任何类型上实例化;它只能使用 foo.cpp 的作者认为提供的模板类的先前版本。

您可能会认为,在编译模板时,编译器应“生成所有版本”,而这些版本在链接过程中从未被过滤出来。除了巨大的开销和极端困难之外,这种方法还会面临,因为“类型修饰”功能(例如指针和阵列)甚至允许仅内置类型产生无限数量的类型,当我现在扩展程序时会发生什么通过添加:

  • baz.cpp
    • 声明并实施类Bazprivate ,并使用 myClass&lt; bazprivate&gt;

否则不可能使用这种方法

  1. 除非我们不得不重新编译 foo, 。 cpp 每次我们更改程序中的任何其他文件,如果它添加了 myclass&lt; t&gt; 的新新颖实例化,
  2. 要求 baz。 cpp 包含(可能通过标头包含) myclass&lt; t&gt; 的完整模板,以便编译器可以在 myClass&lt; bazprivate&gt; > baz.cpp

没有人喜欢(1),因为全程分析汇编系统将 forever 进行编译,并且因为它使得没有源代码的编译库不可能。因此,我们有(2)。

It's because of the requirement for separate compilation and because templates are instantiation-style polymorphism.

Lets get a little closer to concrete for an explanation. Say I've got the following files:

  • foo.h
    • declares the interface of class MyClass<T>
  • foo.cpp
    • defines the implementation of class MyClass<T>
  • bar.cpp
    • uses MyClass<int>

Separate compilation means I should be able to compile foo.cpp independently from bar.cpp. The compiler does all the hard work of analysis, optimization, and code generation on each compilation unit completely independently; we don't need to do whole-program analysis. It's only the linker that needs to handle the entire program at once, and the linker's job is substantially easier.

bar.cpp doesn't even need to exist when I compile foo.cpp, but I should still be able to link the foo.o I already had together with the bar.o I've only just produced, without needing to recompile foo.cpp. foo.cpp could even be compiled into a dynamic library, distributed somewhere else without foo.cpp, and linked with code they write years after I wrote foo.cpp.

"Instantiation-style polymorphism" means that the template MyClass<T> isn't really a generic class that can be compiled to code that can work for any value of T. That would add overhead such as boxing, needing to pass function pointers to allocators and constructors, etc. The intention of C++ templates is to avoid having to write nearly identical class MyClass_int, class MyClass_float, etc, but to still be able to end up with compiled code that is mostly as if we had written each version separately. So a template is literally a template; a class template is not a class, it's a recipe for creating a new class for each T we encounter. A template cannot be compiled into code, only the result of instantiating the template can be compiled.

So when foo.cpp is compiled, the compiler can't see bar.cpp to know that MyClass<int> is needed. It can see the template MyClass<T>, but it can't emit code for that (it's a template, not a class). And when bar.cpp is compiled, the compiler can see that it needs to create a MyClass<int>, but it can't see the template MyClass<T> (only its interface in foo.h) so it can't create it.

If foo.cpp itself uses MyClass<int>, then code for that will be generated while compiling foo.cpp, so when bar.o is linked to foo.o they can be hooked up and will work. We can use that fact to allow a finite set of template instantiations to be implemented in a .cpp file by writing a single template. But there's no way for bar.cpp to use the template as a template and instantiate it on whatever types it likes; it can only use pre-existing versions of the templated class that the author of foo.cpp thought to provide.

You might think that when compiling a template the compiler should "generate all versions", with the ones that are never used being filtered out during linking. Aside from the huge overhead and the extreme difficulties such an approach would face because "type modifier" features like pointers and arrays allow even just the built-in types to give rise to an infinite number of types, what happens when I now extend my program by adding:

  • baz.cpp
    • declares and implements class BazPrivate, and uses MyClass<BazPrivate>

There is no possible way that this could work unless we either

  1. Have to recompile foo.cpp every time we change any other file in the program, in case it added a new novel instantiation of MyClass<T>
  2. Require that baz.cpp contains (possibly via header includes) the full template of MyClass<T>, so that the compiler can generate MyClass<BazPrivate> during compilation of baz.cpp.

Nobody likes (1), because whole-program-analysis compilation systems take forever to compile , and because it makes it impossible to distribute compiled libraries without the source code. So we have (2) instead.

不如归去 2025-01-28 17:20:59

这里有很多正确的答案,但是我想添加此(以进行完整):

如果您在实现CPP文件的底部,请明确对模板将使用的所有类型的实例化,则链接器将能够找到它们照常。

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

template class vector<int>;

这将实例化(从而向链接器提供)类及其所有成员功能(仅)。类似的语法适用于功能模板,因此,如果您的非成员运算符过载,则可能需要为这些操作员进行相同的操作。

上面的示例是没有用的,因为向量已在标题中完全定义,除非一个共同的包含文件(预编译标头?)使用 extern模板类class vector&lt; int&gt; ,以防止其实例化。使用向量的其他(1000?)文件。

Plenty correct answers here, but I wanted to add this (for completeness):

If you, at the bottom of the implementation cpp file, do explicit instantiation of all the types the template will be used with, the linker will be able to find them as usual.

Edit: Adding example of explicit template instantiation. Used after the template has been defined, and all member functions has been defined.

template class vector<int>;

This will instantiate (and thus make available to the linker) the class and all its member functions (only). Similar syntax works for function templates, so if you have non-member operator overloads you may need to do the same for those.

The above example is fairly useless since vector is fully defined in headers, except when a common include file (precompiled header?) uses extern template class vector<int> so as to keep it from instantiating it in all the other (1000?) files that use vector.

我乃一代侩神 2025-01-28 17:20:59

在将它们实际编译为对象代码之前,需要由编译器实例化模板。只有知道模板参数已知,才能实现这种实例化。现在想象一个场景,其中在 ah 中声明模板功能,在 a.cpp 中定义,并在 b.cpp 中使用。当编译 A.CPP 时,不一定知道即将进行的汇编 b.cpp 将需要模板的实例,更不用说哪个特定实例是。对于更多标题和源文件,情况很快就会变得更加复杂。

可以说,可以使编译器更聪明地“向前看”模板的所有用途,但是我敢肯定,创建递归或其他复杂的情况并不困难。 Afaik,编译器不会这样做。正如安东指出的那样,一些编译器支持模板实例化的明确导出声明,但并非所有编译器都支持它(尚未?)。

Templates need to be instantiated by the compiler before actually compiling them into object code. This instantiation can only be achieved if the template arguments are known. Now imagine a scenario where a template function is declared in a.h, defined in a.cpp and used in b.cpp. When a.cpp is compiled, it is not necessarily known that the upcoming compilation b.cpp will require an instance of the template, let alone which specific instance would that be. For more header and source files, the situation can quickly get more complicated.

One can argue that compilers can be made smarter to "look ahead" for all uses of the template, but I'm sure that it wouldn't be difficult to create recursive or otherwise complicated scenarios. AFAIK, compilers don't do such look aheads. As Anton pointed out, some compilers support explicit export declarations of template instantiations, but not all compilers support it (yet?).

淡笑忘祈一世凡恋 2025-01-28 17:20:59

实际上,在C ++ 11之前,标准定义导出关键字,将使将使在标头文件中声明模板并在其他地方实现模板成为可能。以某种说话的方式。并非真的,作为唯一实现的指出了

幻影优势#1:隐藏源代码。许多用户说,他们希望通过使用出口来
不再需要为成员/非会员功能模板和类成员功能运送定义
模板。这不是真的。使用导出,图书馆作者仍然必须运送完整的模板源代码或其直接
等效(例如,特定于系统的解析树),因为实例化需要完整的信息。 [...]

幻影优势#2:快速构建,减少依赖关系。许多用户期望导出将允许真正的单独
他们期望的对象代码的汇编将允许更快的构建。不是因为
导出模板的汇编确实是独立的,但不是对象代码。相反,出口几乎总是使
构建较慢,因为至少必须在预链接时间进行相同数量的编译工作。出口
甚至不会减少模板定义之间的依赖关系,因为依赖性是固有的,
独立于文件组织。

没有一个流行的编译器实施此关键字。该功能的唯一实现是在Edison设计组编写的前端中,该组由Comeau C ++编译器使用。所有其他要求您在标题文件中编写模板,因为编译器需要模板定义进行适当的实例化(正如其他人已经指出的那样)。

结果,ISO C ++标准委员会决定删除使用C ++ 11的模板的导出功能。

Actually, prior to C++11 the standard defined the export keyword that would make it possible to declare templates in a header file and implement them elsewhere. In a manner of speaking. Not really, as the only ones who ever implemented that feature pointed out:

Phantom advantage #1: Hiding source code. Many users, have said that they expect that by using export they will
no longer have to ship definitions for member/nonmember function templates and member functions of class
templates. This is not true. With export, library writers still have to ship full template source code or its direct
equivalent (e.g., a system-specific parse tree) because the full information is required for instantiation. [...]

Phantom advantage #2: Fast builds, reduced dependencies. Many users expect that export will allow true separate
compilation of templates to object code which they expect would allow faster builds. It doesn’t because the
compilation of exported templates is indeed separate but not to object code. Instead, export almost always makes
builds slower, because at least the same amount of compilation work must still be done at prelink time. Export
does not even reduce dependencies between template definitions because the dependencies are intrinsic,
independent of file organization.

None of the popular compilers implemented this keyword. The only implementation of the feature was in the frontend written by the Edison Design Group, which is used by the Comeau C++ compiler. All others required you to write templates in header files, because the compiler needs the template definition for proper instantiation (as others pointed out already).

As a result, the ISO C++ standard committee decided to remove the export feature of templates with C++11.

南风几经秋 2025-01-28 17:20:59

尽管标准C ++没有此类要求,但一些编译器要求在使用它们使用的每个翻译单元中都需要提供所有功能和类模板。实际上,对于这些编译器,必须在标题文件中提供模板功能的物体。重复:这意味着这些编译器不允许在非头部文件(例如.cpp文件)中定义它们,

那里有一个 enfort 关键字,应该减轻此问题,但它远不近地是便携式的。

Although standard C++ has no such requirement, some compilers require that all function and class templates need to be made available in every translation unit they are used. In effect, for those compilers, the bodies of template functions must be made available in a header file. To repeat: that means those compilers won't allow them to be defined in non-header files such as .cpp files

There is an export keyword which is supposed to mitigate this problem, but it's nowhere close to being portable.

傲世九天 2025-01-28 17:20:59

模板通常在标题中使用,因为编译器需要根据模板参数给出/推论的参数实例化代码的不同版本,并且更容易(作为程序员)让编译器多次重新编译相同的代码并稍后进行。 。
请记住,模板不能直接表示代码,而是该代码多个版本的模板。
当您在 .cpp 文件中编译非模板函数时,您正在编译混凝土函数/类。
模板不是这种情况,可以用不同类型实例化,即,在用混凝土类型替换模板参数时,必须发出具体代码。

导出关键字有一个功能,该功能用于单独编译。
导出功能在 c ++ 11 和afaik中弃用,只有一个编译器实现了它。
您不应使用导出
c ++ c ++ 11 中不可能单独汇编,但也许在 c ++ 17 中,如果概念进入,我们可以拥有某种单独汇编的方式。

为了实现单独的汇编,必须进行单独的模板身体检查。
看来可以解决概念的解决方案。
看看这个最近呈现在
标准委员会会议。
我认为这不是唯一的要求,因为您仍然需要在用户代码中实例化模板代码的代码。

我猜想模板的单独汇编问题也是一个问题,该问题正在迁移到模块,这是当前正在使用的问题。

编辑:截至2020年8月起,模块已经成为C ++的现实: https://en.cppreference .com/w/cpp/语言/模块

Templates are often used in headers because the compiler needs to instantiate different versions of the code, depending on the parameters given/deduced for template parameters, and it's easier (as a programmer) to let the compiler recompile the same code multiple times and deduplicate later.
Remember that a template doesn't represent code directly, but a template for several versions of that code.
When you compile a non-template function in a .cpp file, you are compiling a concrete function/class.
This is not the case for templates, which can be instantiated with different types, namely, concrete code must be emitted when replacing template parameters with concrete types.

There was a feature with the export keyword that was meant to be used for separate compilation.
The export feature is deprecated in C++11 and, AFAIK, only one compiler implemented it.
You shouldn't make use of export.
Separate compilation is not possible in C++ or C++11 but maybe in C++17, if concepts make it in, we could have some way of separate compilation.

For separate compilation to be achieved, separate template body checking must be possible.
It seems that a solution is possible with concepts.
Take a look at this paper recently presented at the
standards committee meeting.
I think this is not the only requirement, since you still need to instantiate code for the template code in user code.

The separate compilation problem for templates I guess it's also a problem that is arising with the migration to modules, which is currently being worked.

EDIT: As of August 2020 Modules are already a reality for C++: https://en.cppreference.com/w/cpp/language/modules

亚希 2025-01-28 17:20:59

即使上面有很多很好的解释,我还是缺少一种将模板分为标题和身体的实用方法。

我主要关注的是,当我更改其定义时,避免重新编译所有模板用户。

对于我来说,在模板主体中拥有所有模板实例化并不是一个可行的解决方案,因为模板作者可能不知道其使用情况和模板用户是否没有权利进行修改。

我采用了以下方法,该方法也适用于较旧的编译器(GCC 4.3.4,ACC A.03.13)。

对于每个模板使用情况,其自己的标头文件中都有一个Typedef(从UML模型生成)。它的身体包含实例化(最终在末尾链接的库中)。

模板的每个用户都包含该标头文件并使用Typedef。

示意图示例:

myTemplate.h:

#ifndef MyTemplate_h
#define MyTemplate_h 1

template <class T>
class MyTemplate
{
public:
  MyTemplate(const T& rt);
  void dump();
  T t;
};

#endif

myTemplate.cpp:

#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;
}

myInstantiatedTemplate.h:

#ifndef MyInstantiatedTemplate_h
#define MyInstantiatedTemplate_h 1
#include "MyTemplate.h"

typedef MyTemplate< int > MyInstantiatedTemplate;

#endif

myInstantiatedTemplate.cpp:

#include "MyTemplate.cpp"

template class MyTemplate< int >;

main.cpp:

#include "MyInstantiatedTemplate.h"

int main()
{
  MyInstantiatedTemplate m(100);
  m.dump();
  return 0;
}

这样,只有模板实例化才需要重新编译,而不是所有模板用户(和依赖项)。

Even though there are plenty of good explanations above, I'm missing a practical way to separate templates into header and body.

My main concern is avoiding recompilation of all template users, when I change its definition.

Having all template instantiations in the template body is not a viable solution for me, since the template author may not know all if its usage and the template user may not have the right to modify it.

I took the following approach, which works also for older compilers (gcc 4.3.4, aCC A.03.13).

For each template usage there's a typedef in its own header file (generated from the UML model). Its body contains the instantiation (which ends up in a library which is linked in at the end).

Each user of the template includes that header file and uses the typedef.

A schematic example:

MyTemplate.h:

#ifndef MyTemplate_h
#define MyTemplate_h 1

template <class T>
class MyTemplate
{
public:
  MyTemplate(const T& rt);
  void dump();
  T t;
};

#endif

MyTemplate.cpp:

#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;
}

MyInstantiatedTemplate.h:

#ifndef MyInstantiatedTemplate_h
#define MyInstantiatedTemplate_h 1
#include "MyTemplate.h"

typedef MyTemplate< int > MyInstantiatedTemplate;

#endif

MyInstantiatedTemplate.cpp:

#include "MyTemplate.cpp"

template class MyTemplate< int >;

main.cpp:

#include "MyInstantiatedTemplate.h"

int main()
{
  MyInstantiatedTemplate m(100);
  m.dump();
  return 0;
}

This way only the template instantiations will need to be recompiled, not all template users (and dependencies).

难得心□动 2025-01-28 17:20:59

这意味着定义模板类实现方法实现的最便携方法是在模板类定义中定义它们。

template < typename ... >
class MyClass
{

    int myMethod()
    {
       // Not just declaration. Add method implementation here
    }
};

It means that the most portable way to define method implementations of template classes is to define them inside the template class definition.

template < typename ... >
class MyClass
{

    int myMethod()
    {
       // Not just declaration. Add method implementation here
    }
};
帅的被狗咬 2025-01-28 17:20:59

在编译步骤中使用模板时,编译器将为每个模板实例化生成代码。
在编译和链接过程中,.CPP文件转换为纯对象或机器代码,其中包含引用或未定义的符号,因为Main.cpp中包含的.H文件尚无实现。这些已准备好与另一个定义模板实现的对象文件链接,因此您拥有完整的A.Out可执行文件。

但是,由于需要在编译步骤中处理模板,以便为您定义的每个模板实例化生成代码,因此,仅将模板与标头文件分开的模板不起作用,因为它们始终是手工和手工的,原因是从字面上看,每个模板实例化是一个全新的类。在常规类中,您可以将.h和.cpp分开,因为.h是该类的蓝图,并且.cpp是原始实现,因此可以定期编译和链接任何实现文件,但是使用模板.h是如何蓝图的蓝图。该类不应看起来对象的外观含义为模板.cpp文件不是类的原始定期实现,而只是类的蓝图,因此无法编译.H模板文件的任何实现,因为您需要一些具体的编译,从这种意义上讲,模板是抽象的。

因此,模板永远不会单独编译,只有在其他源文件中具有混凝土实例化的任何地方才进行编译。但是,具体的实例化需要知道模板文件的实现,因为只需使用.h文件中的混凝土类型修改 typename t 就不会完成工作,因为什么.cpp在那里。要链接,我以后再也找不到它,因为记住模板是抽象的,无法编译,因此我现在被迫立即进行实施,所以我知道要编译和链接,现在我已经实现了它链接到封闭源文件中。基本上,当我实例化模板时,我需要创建一个全新的课程,如果我不知道使用我提供的类型时该类别的样子,除非我注意到编译器的编译器,否模板实现,因此现在编译器可以用我的类型替换 t ,并创建一个可以编译和链接的具体类。

综上所述,模板是类应该看起来的蓝图,类是对象应该看起来的蓝图。
我无法编译模板与它们的具体实例化分开,因为编译器仅编译至少在C ++中的混凝土类型是纯语言抽象。可以这么说,我们必须删除模板,我们这样做是为了处理它们的混凝土类型,以便我们的模板抽象可以转换为常规类文件,然后可以正常编译。将模板.h文件和模板.cpp文件分开是毫无意义的。这是荒谬的,因为.cpp和.h的分离仅是可以单独编译和单独链接的.cpp的地方,因为我们不能单独编译它们,因为模板是一种抽象,因此我们始终被迫被迫将抽象始终与混凝土实例化的混凝土实例放在一起,其中始终必须了解所使用的类型。

含义 typename t 在编译步骤中替换了get get而不是链接步骤,因此,如果我尝试在没有 t 的情况下编译模板,则被替换为完全意义的具体值类型编译器和结果对象代码无法创建,因为它不知道 t 是什么。

从技术上讲,创建某种功能可以保存模板。CPP文件并在其他来源找到类型时切换出来,我认为标准确实具有关键字 export 允许您将模板放入单独的CPP文件中,但没有多少编译器实际上实现了这一点。

只是一个旁注,在为模板类专业时,您可以将标头与实现分开,因为按定义进行专业化意味着我专门针对可以单独编译和链接的混凝土类型。

The compiler will generate code for each template instantiation when you use a template during the compilation step.
In the compilation and linking process .cpp files are converted to pure object or machine code which in them contains references or undefined symbols because the .h files that are included in your main.cpp have no implementation YET. These are ready to be linked with another object file that defines an implementation for your template and thus you have a full a.out executable.

However since templates need to be processed in the compilation step in order to generate code for each template instantiation that you define, so simply compiling a template separate from it's header file won't work because they always go hand and hand, for the very reason that each template instantiation is a whole new class literally. In a regular class you can separate .h and .cpp because .h is a blueprint of that class and the .cpp is the raw implementation so any implementation files can be compiled and linked regularly, however using templates .h is a blueprint of how the class should look not how the object should look meaning a template .cpp file isn't a raw regular implementation of a class, it's simply a blueprint for a class, so any implementation of a .h template file can't be compiled because you need something concrete to compile, templates are abstract in that sense.

Therefore templates are never separately compiled and are only compiled wherever you have a concrete instantiation in some other source file. However, the concrete instantiation needs to know the implementation of the template file, because simply modifying the typename T using a concrete type in the .h file is not going to do the job because what .cpp is there to link, I can't find it later on because remember templates are abstract and can't be compiled, so I'm forced to give the implementation right now so I know what to compile and link, and now that I have the implementation it gets linked into the enclosing source file. Basically, the moment I instantiate a template I need to create a whole new class, and I can't do that if I don't know how that class should look like when using the type I provide unless I make notice to the compiler of the template implementation, so now the compiler can replace T with my type and create a concrete class that's ready to be compiled and linked.

To sum up, templates are blueprints for how classes should look, classes are blueprints for how an object should look.
I can't compile templates separate from their concrete instantiation because the compiler only compiles concrete types, in other words, templates at least in C++, is pure language abstraction. We have to de-abstract templates so to speak, and we do so by giving them a concrete type to deal with so that our template abstraction can transform into a regular class file and in turn, it can be compiled normally. Separating the template .h file and the template .cpp file is meaningless. It is nonsensical because the separation of .cpp and .h only is only where the .cpp can be compiled individually and linked individually, with templates since we can't compile them separately, because templates are an abstraction, therefore we are always forced to put the abstraction always together with the concrete instantiation where the concrete instantiation always has to know about the type being used.

Meaning typename T get's replaced during the compilation step not the linking step so if I try to compile a template without T being replaced as a concrete value type that is completely meaningless to the compiler and as a result object code can't be created because it doesn't know what T is.

It is technically possible to create some sort of functionality that will save the template.cpp file and switch out the types when it finds them in other sources, I think that the standard does have a keyword export that will allow you to put templates in a separate cpp file but not that many compilers actually implement this.

Just a side note, when making specializations for a template class, you can separate the header from the implementation because a specialization by definition means that I am specializing for a concrete type that can be compiled and linked individually.

得不到的就毁灭 2025-01-28 17:20:59

如果担心是通过使用它来编译.H作为所有.CPP模块的一部分而产生的额外汇编时间和二进制尺寸膨胀,那么在许多情况下,您可以做的就是使模板类从非策略的基础类中下降。接口的非类型依赖性部分,该基类可以在.cpp文件中实现。

If the concern is the extra compilation time and binary size bloat produced by compiling the .h as part of all the .cpp modules using it, in many cases what you can do is make the template class descend from a non-templatized base class for non type-dependent parts of the interface, and that base class can have its implementation in the .cpp file.

软的没边 2025-01-28 17:20:59

只是为了在这里添加一些值得注意的东西。当不是功能模板时,可以在实现文件中定义模板类的方法。


myqueue.hpp:

template <class T> 
class QueueA {
    int size;
    ...
public:
    template <class T> T dequeue() {
       // implementation here
    }

    bool isEmpty();

    ...
}    

myqueue.cpp:

// implementation of regular methods goes like this:
template <class T> bool QueueA<T>::isEmpty() {
    return this->size == 0;
}


main()
{
    QueueA<char> Q;

    ...
}

Just to add something noteworthy here. One can define methods of a templated class just fine in the implementation file when they are not function templates.


myQueue.hpp:

template <class T> 
class QueueA {
    int size;
    ...
public:
    template <class T> T dequeue() {
       // implementation here
    }

    bool isEmpty();

    ...
}    

myQueue.cpp:

// implementation of regular methods goes like this:
template <class T> bool QueueA<T>::isEmpty() {
    return this->size == 0;
}


main()
{
    QueueA<char> Q;

    ...
}
ペ泪落弦音 2025-01-28 17:20:59

单独实现的一种方法如下。

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>

innion_foo.h 具有正向声明。 foo.tpp 具有实现,并包括 innion_foo.h ; foo.h 只有一行,包括 foo.tpp

在编译时,将 foo.h 的内容复制到 foo.tpp ,然后将整个文件复制到 foo.h 之后编译。这样,没有限制,命名是一致的,以换取一个额外的文件。

我之所以这样做,是因为代码中断的静态分析仪在*。TPP 中看不到类的前面声明时。在任何IDE或使用YouCompleteme或其他内容中编写代码时,这很烦人。

A way to have separate implementation is as follows.

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>

inner_foo.h has the forward declarations. foo.tpp has the implementation and includes inner_foo.h; and foo.h will have just one line, to include foo.tpp.

On compile time, contents of foo.h are copied to foo.tpp and then the whole file is copied to foo.h after which it compiles. This way, there is no limitations, and the naming is consistent, in exchange for one extra file.

I do this because static analyzers for the code break when it does not see the forward declarations of class in *.tpp. This is annoying when writing code in any IDE or using YouCompleteMe or others.

如痴如狂 2025-01-28 17:20:59

这是完全正确的,因为编译器必须知道其分配类型。因此,如果要公开或库的一部分(静态或动态),则模板类,功能,枚举等也必须在标题文件中实现是。如果编译器不知道该类型是无法编译的。在.NET中,它可以是因为所有对象都来自对象类。这不是.NET。

That is exactly correct because the compiler has to know what type it is for allocation. So template classes, functions, enums,etc.. must be implemented as well in the header file if it is to be made public or part of a library (static or dynamic) because header files are NOT compiled unlike the c/cpp files which are. If the compiler doesn't know the type is can't compile it. In .Net it can because all objects derive from the Object class. This is not .Net.

五里雾 2025-01-28 17:20:59

我建议查看此GCC页面,讨论了模板实例化的“ Cfront”和“ Borland”模型之间的权衡。

https://gcc.gnu.gnu.org.org.org.org/onlinedocs/gcc -4.6.4/gcc/template-instantiation.html

“ Borland”模型对应于作者所建议的内容,提供完整的模板定义,并多次编译事物。

它包含有关使用手动和自动模板实例化的明确建议。例如,“ -repo”选项可用于收集需要实例化的模板。或其他选择是使用“ -fno-implicer-templates”禁用自动模板实例化以强制手动模板实例化。

根据我的经验,我依靠C ++标准库,并为每个编译单元(使用模板库)实例化模板。对于我的大型模板类,我对所需类型进行手动模板实例化。

这是我的方法,因为我提供了一个工作程序,而不是用于其他程序中的模板库。这本书的作者Josuttis的作者在模板库上工作了很多。

如果我真的担心速度,我想我会使用预编译的标头探索
https://gcc.gnu.org.org/onlinedocs/gcc/gcc/precompiled-headers.html

在许多编译器中获得支持。但是,我认为模板标头文件很难预编译标头。

I suggest looking at this gcc page which discusses the tradeoffs between the "cfront" and "borland" model for template instantiations.

https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Template-Instantiation.html

The "borland" model corresponds to what the author suggests, providing the full template definition, and having things compiled multiple times.

It contains explicit recommendations concerning using manual and automatic template instantiation. For example, the "-repo" option can be used to collect templates which need to be instantiated. Or another option is to disable automatic template instantiations using "-fno-implicit-templates" to force manual template instantiation.

In my experience, I rely on the C++ Standard Library and Boost templates being instantiated for each compilation unit (using a template library). For my large template classes, I do manual template instantiation, once, for the types I need.

This is my approach because I am providing a working program, not a template library for use in other programs. The author of the book, Josuttis, works a lot on template libraries.

If I was really worried about speed, I suppose I would explore using Precompiled Headers
https://gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html

which is gaining support in many compilers. However, I think precompiled headers would be difficult with template header files.

香草可樂 2025-01-28 17:20:59

”此处复制

(从a .cpp 文件中使用我的所有功能,无论它们是模板函数还是常规功能。并且有一种方法可以使用一些基本 #ifndef 魔术。这是您可以做的:

main.cpp

#include "myclass.hpp"

int main()
{
  // ...
}

myclass.hpp

#ifndef MYCLASS
#define MYCLASS

template<class T>
class MyClass
{
  T val;
public:
  MyClass(T val_);
}

#define MYCLASS_FUNCTIONS
#include "myclass.cpp"

#endif

myclass.cpp

#ifndef MYCLASS_FUNCTIONS
#include "myclass.hpp"

// regular functions:
// ...

#else
 
// template functions:
template<class T>
MyClass<T>::MyClass(T val_)
    :val(val_)
{}

// ...
#endif

这是预编译器如何看待它。我们有两个 .cpp 文件。

  1. 当我们编译main.cpp时,我们:
  2. include myClass.hpp
  3. 检查 myClass 不确定,它是
  4. 定义的,它
  5. 为编译器提供了生成类的定义(来自模板类)
  6. 包括 myClass.cpp
  7. 定义 myClass_functions
  8. 检查是否定义了 myClass_functions ,它是定义的,
  9. 给出了编译器的定义(从模板函数)
  10. 在我们时 compile myclass.cpp
  11. 检查是否定义了 myClass_functions ,它不
  12. 包括 myClass.hpp
  13. 检查 myclass not n defined n n
  14. dewindine
  15. 给编译器类的定义
  16. 包括 myClass.cpp
  17. include myClass.hpp ye Reme
  18. this Time myClass 定义了什么都不在内部做任何事情,请返回<<代码> myClass.cpp
  19. 检查是否定义了 myClass_functions ,它为
  20. 编译器提供了生成的函数的定义(来自模板函数)
  21. 退出包括两次
  22. 通过到编译器的所有常规函数

(copying here from a closed duplicate)

I prefer to have all of my functions in the .cpp file, regardless of whether they are template functions or regular functions. And there is a way to do that with some basic #ifndef magic. Here's what you can do:

main.cpp

#include "myclass.hpp"

int main()
{
  // ...
}

myclass.hpp

#ifndef MYCLASS
#define MYCLASS

template<class T>
class MyClass
{
  T val;
public:
  MyClass(T val_);
}

#define MYCLASS_FUNCTIONS
#include "myclass.cpp"

#endif

myclass.cpp

#ifndef MYCLASS_FUNCTIONS
#include "myclass.hpp"

// regular functions:
// ...

#else
 
// template functions:
template<class T>
MyClass<T>::MyClass(T val_)
    :val(val_)
{}

// ...
#endif

Here's how the precompiler sees it. We have two .cpp files.

  1. When we compile main.cpp we:
  2. include myclass.hpp
  3. check that MYCLASS is undefined, and it is
  4. define it
  5. give compiler the definitions of the generated class (from template class)
  6. include myclass.cpp
  7. define MYCLASS_FUNCTIONS
  8. check if MYCLASS_FUNCTIONS is defined, it is
  9. give compiler the definitions of the generated functions (from template functions)
  10. When we compile myclass.cpp
  11. check if MYCLASS_FUNCTIONS is defined, it isn't
  12. include myclass.hpp
  13. check that MYCLASS is undefined, and it is
  14. define it
  15. give compiler the definitions of the class
  16. include myclass.cpp
  17. include myclass.hpp again
  18. this time MYCLASS is defined so do nothing inside, return to myclass.cpp
  19. check if MYCLASS_FUNCTIONS is defined, it is
  20. give compiler the definition of the generated functions (from template functions)
  21. exit include twice
  22. pass to the compiler all the regular functions
黎夕旧梦 2025-01-28 17:20:59

Moshe回答的动机来自: https://stackoverflow.com/a/3844448106/6459849

我一边的小贡献有一个扩展示例。假设有一个整体操作核心,它包含一个响应成功率,其中包含通用类型。

响应响应

template <class T>
class ResponseSuccess {
public:
    ResponseSuccess(const ResponseStatus responseStatus, const T& data) :
        m_responseStatus(responseStatus),
        m_data(data) {}

    ~ResponseSuccess() = default;

    // Basis requirement, have Copy/Move constructor/delete assignment operator

    ResponseStatus getResponseStatus() const {
        return m_responseStatus;
    }

    T getData() const {
        return m_data;
    };

private:
    ResponseStatus m_responseStatus;

    T m_data;
};

Operationsuccess.h

template <class T>
class OperationResponse {
public:
    explicit OperationResponse(ResponseSuccess<T> responseSuccess) :
        m_responseSuccess(std::move(responseSuccess)) {}

    ~OperationResponse() = default;
    // Basis requirement, have Copy/Move constructor/delete assignment operator

    ResponseSuccess<T> getResponseSuccess() const {
        return m_responseSuccess;
    }

private:
    ResponseSuccess<T> m_responseSuccess;
    // have a failure, in case required
};

用法:

MyObject myObj(<ctor_args>);
    ResponseSuccess<MyObject> responseSuccess(ResponseStatus::SUCCESS, myObj);
    OperationResponse<MyObject> successOperationResponse(responseSuccess);
..
// Fetches the response -> successOperationResponse.getResponseSuccess();

With motivation from Moshe's answer from: https://stackoverflow.com/a/38448106/6459849

A small contribution from my side with an extended example. Let's say there is an overall OperationSuccess and it contains a ResponseSuccess which has a generic type in it.

ResponseSuccess.h

template <class T>
class ResponseSuccess {
public:
    ResponseSuccess(const ResponseStatus responseStatus, const T& data) :
        m_responseStatus(responseStatus),
        m_data(data) {}

    ~ResponseSuccess() = default;

    // Basis requirement, have Copy/Move constructor/delete assignment operator

    ResponseStatus getResponseStatus() const {
        return m_responseStatus;
    }

    T getData() const {
        return m_data;
    };

private:
    ResponseStatus m_responseStatus;

    T m_data;
};

OperationSuccess.h

template <class T>
class OperationResponse {
public:
    explicit OperationResponse(ResponseSuccess<T> responseSuccess) :
        m_responseSuccess(std::move(responseSuccess)) {}

    ~OperationResponse() = default;
    // Basis requirement, have Copy/Move constructor/delete assignment operator

    ResponseSuccess<T> getResponseSuccess() const {
        return m_responseSuccess;
    }

private:
    ResponseSuccess<T> m_responseSuccess;
    // have a failure, in case required
};

Usage:

MyObject myObj(<ctor_args>);
    ResponseSuccess<MyObject> responseSuccess(ResponseStatus::SUCCESS, myObj);
    OperationResponse<MyObject> successOperationResponse(responseSuccess);
..
// Fetches the response -> successOperationResponse.getResponseSuccess();
李白 2025-01-28 17:20:59

C ++ 20允许 auto 作为函数参数类型,

void func(auto param) {
  // do stuff
}

这使缩写功能模板。编译器还需要访问该函数的实现,以便将其实例化。因此,实现应在标头文件中。

使用 auto 参数无法将分离的实现保持在源文件中的替代解决方案。如果需要, auto 参数可以用键入的模板参数替换。

// func.hh

template <typename T>
void func(T param);
// func.cc

template <typename T>
void func(T param) {            // implementation
  // do stuff
}

template void func(int param);  // explicit instantiation for int parameter
// main.cc

#include <func.hh>

int main() {
  func(1);    // ok
  func(1LL);  // link error
}

// cc func.cc main.cc

C++20 allows auto as a function parameter type

void func(auto param) {
  // do stuff
}

This makes an Abbreviated function template. The compiler also needs to have access to the implementation of the function to instantiate it with the deduced parameter type. Therefore, the implementation should be in a header file.

The alternative solution of keeping the separated implementation in a source file is not possible with auto parameters. If this is required, auto parameters can be replaced with typed template parameters.

// func.hh

template <typename T>
void func(T param);
// func.cc

template <typename T>
void func(T param) {            // implementation
  // do stuff
}

template void func(int param);  // explicit instantiation for int parameter
// main.cc

#include <func.hh>

int main() {
  func(1);    // ok
  func(1LL);  // link error
}

// cc func.cc main.cc
記憶穿過時間隧道 2025-01-28 17:20:59

在标题文件中同时编写声明和定义是一个好主意的另一个原因是可读性。 这样的模板函数

template <class T>
T min(T const& one, T const& theOther);

假设Utility.h: and the Utility.cpp中

#include "Utility.h"
template <class T>
T min(T const& one, T const& other)
{
    return one < other ? one : other;
}

有 。当您比较没有实现“&lt;”的两个类实例时,它将丢弃编译器错误。

因此,如果将模板声明和定义分开,您将无法仅读取标题文件以查看此模板的ins and outs以便在您自己的类上使用此API,尽管编译器会在此中告诉您需要覆盖哪个操作员的情况。

Another reason that it's a good idea to write both declarations and definitions in header files is for readability. Suppose there's such a template function in Utility.h:

template <class T>
T min(T const& one, T const& theOther);

And in the Utility.cpp:

#include "Utility.h"
template <class T>
T min(T const& one, T const& other)
{
    return one < other ? one : other;
}

This requires every T class here to implement the less than operator (<). It will throw a compiler error when you compare two class instances that haven't implemented the "<".

Therefore if you separate the template declaration and definition, you won't be able to only read the header file to see the ins and outs of this template in order to use this API on your own classes, though the compiler will tell you in this case about which operator needs to be overridden.

如日中天 2025-01-28 17:20:59

我不得不编写一个模板和此示例对我有用,

这是一个动态数组类的示例。

#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文件,您可以定义您的函数。

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];
    }

I had to write a template class an d this example worked for me

Here is an example of this for a dynamic array class.

#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

Now inside you .template file you define your functions just how you normally would.

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];
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文