C ++標準ライブラリからの引用:チュートリアルとハンドブック:
現在、テンプレートを使用する唯一の移植可能な方法は、インライン関数を使用してヘッダーファイルにテンプレートを実装することです。
どうしてこれなの?
(明確化:ヘッダーファイルは唯一のポータブルソリューションではありませんが、最も便利なポータブルソリューションです。)
C ++標準ライブラリからの引用:チュートリアルとハンドブック:
現在、テンプレートを使用する唯一の移植可能な方法は、インライン関数を使用してヘッダーファイルにテンプレートを実装することです。
どうしてこれなの?
(明確化:ヘッダーファイルは唯一のポータブルソリューションではありませんが、最も便利なポータブルソリューションです。)
回答:
警告:ヘッダーファイルに実装を含める必要はありません。この回答の最後にある代替ソリューションを参照してください。
とにかく、コードが失敗する理由は、テンプレートをインスタンス化するときに、コンパイラが指定されたテンプレート引数を使用して新しいクラスを作成するためです。例えば:
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をご覧ください。
ここにはたくさんの正しい答えがありますが、これを追加したいと思います(完全を期すため)。
実装cppファイルの下部で、テンプレートで使用されるすべてのタイプの明示的なインスタンス化を行うと、リンカーは通常どおりそれらを見つけることができます。
編集:明示的なテンプレートのインスタンス化の例を追加します。テンプレートが定義され、すべてのメンバー関数が定義された後に使用されます。
template class vector<int>;
これにより、クラスとそのすべてのメンバー関数(のみ)がインスタンス化されます(リンカで使用できるようになります)。テンプレート関数でも同様の構文が機能するため、メンバー以外の演算子のオーバーロードがある場合は、それらに対して同じことを行う必要がある場合があります。
上記の例は、ベクターがヘッダーで完全に定義されているため、ほとんど役に立ちません。ただし、共通のインクルードファイル(プリコンパイル済みヘッダー?)がベクターを使用する他のextern template class vector<int>
すべての(1000?)ファイルでインスタンス化されないようにする場合を除きます。
type
手動でリストしなくてもクラスを使用できるようにすることを目的としたテンプレートの目的に反します。
vector
コンテナは本質的に「すべての」タイプをターゲットにしているため、良い例ではありません。ただし、特定のタイプのセット(例えば、数値タイプ:int8_t、int16_t、int32_t、uint8_t、uint16_tなど)専用のテンプレートを作成することは非常に頻繁に発生します。この場合でも、テンプレートを使用することには意味があります、しかし、型のセット全体に対してそれらを明示的にインスタンス化することも可能であり、私の意見では推奨されています。
.cpp
ファイルに入れ、2つのインスタンス化が他の.cpp
ファイルから参照されていますが、それでもメンバーが見つからないというリンクエラーが発生します。
これは、個別にコンパイルする必要があることと、テンプレートがインスタンス化スタイルの多態性であるためです。
説明のために、もう少し具体的に見ていきましょう。次のファイルがあるとします。
class MyClass<T>
class MyClass<T>
MyClass<int>
私はコンパイルすることができるはず分割コンパイル手段foo.cppを独立してからbar.cpp。コンパイラーは、分析、最適化、およびコード生成のハードワークをすべて、各コンパイル単位で完全に独立して実行します。プログラム全体の分析を行う必要はありません。プログラム全体を一度に処理する必要があるのはリンカだけであり、リンカの仕事はかなり簡単です。
bar.cppはさえ、私はコンパイル時に存在する必要はありませんfoo.cppを、私はまだリンクすることができるはずfoo.oの私はすでにと一緒にいたbar.o再コンパイルする必要がなく、私はちょうど生成しましたが、FOO .cpp。foo.cppは、動的ライブラリにコンパイルし、foo.cppなしで別の場所に配布し、私がfoo.cppを作成してから数年後に作成したコードにリンクすることもできます。
「インスタンス化スタイルのポリモーフィズム」とは、テンプレートMyClass<T>
が実際には、任意の値で機能するコードにコンパイルできる汎用クラスではないことを意味しますT
。これにより、ボクシング、アロケーターやコンストラクターに関数ポインターを渡す必要などのオーバーヘッドが追加されます。C++テンプレートの目的はclass MyClass_int
、ほぼ同一のclass MyClass_float
、などを記述する必要を回避することですが、次のようなコンパイル済みコードになる可能性があります。私たちは、主にいるかのようにしていた個別の各バージョンが書かれて。したがって、テンプレートは文字通りテンプレートです。クラステンプレートはクラスではありませんT
。これは、遭遇するそれぞれに新しいクラスを作成するためのレシピです。テンプレートをコードにコンパイルすることはできません。コンパイルできるのは、テンプレートのインスタンス化の結果のみです。
したがって、foo.cppがコンパイルされると、コンパイラーはbar.cppを見てそれMyClass<int>
が必要であることを知ることができません。テンプレートを見ることができますがMyClass<T>
、そのためのコードを出力することはできません(クラスではなくテンプレートです)。そして、bar.cppがコンパイルされると、コンパイラーはを作成する必要があることを認識できますがMyClass<int>
、テンプレートMyClass<T>
(foo.hのインターフェースのみ)を認識できないため、テンプレートを作成できません。
foo.cpp自体がを使用する場合MyClass<int>
、そのコードはfoo.cppのコンパイル中に生成されます。そのため、bar.oがfoo.oにリンクされている場合は、それらをフックして機能します。この事実を利用して、単一のテンプレートを作成することで、テンプレートのインスタンス化の有限セットを.cppファイルに実装できます。しかし、bar.cppがテンプレートをテンプレートとして使用し、好きなタイプでインスタンス化する方法はありません。foo.cppの作者が提供しようと考えていたテンプレートクラスの既存のバージョンのみを使用できます。
テンプレートをコンパイルするとき、コンパイラは「すべてのバージョンを生成する」べきだと思うかもしれません。リンク中に使用されないものは除外されます。ポインタや配列などの「型修飾子」機能により、組み込み型でさえ無限の型を生じさせることができるため、巨大なオーバーヘッドと極端な困難を除いて、このようなアプローチは直面します。プログラムを拡張するとどうなりますか。追加することにより:
class BazPrivate
、および使用MyClass<BazPrivate>
これがうまくいかない方法はありません。
MyClass<T>
MyClass<T>
MyClass<BazPrivate>
プログラム分析全体のコンパイルシステムはcompileに永遠にかかるため、ソースコードなしでコンパイルされたライブラリを配布することが不可能になるため、誰も(1)を好みません。したがって、代わりに(2)があります。
テンプレートは、実際にオブジェクトコードにコンパイルする前に、コンパイラによってインスタンス化する必要があります。このインスタンス化は、テンプレートの引数がわかっている場合にのみ実行できます。テンプレート関数がで宣言されa.h
、で定義されa.cpp
、使用されるシナリオを想像してくださいb.cpp
。a.cpp
がコンパイルされるとき、次のコンパイルでb.cpp
テンプレートのインスタンスが必要になることは、必ずしも特定のインスタンスが必要になることは言うまでもありません。ヘッダーファイルとソースファイルが増えると、状況はすぐに複雑になります。
コンパイラーはテンプレートのすべての使用に対して「先読み」するように賢くできると主張することができますが、再帰的またはその他の複雑なシナリオを作成することは難しくないと確信しています。私の知る限り、コンパイラはそのような先読みを行いません。アントンが指摘したように、一部のコンパイラーはテンプレートのインスタンス化の明示的なエクスポート宣言をサポートしていますが、すべてのコンパイラーがサポートしているわけではありません(まだ?)。
実際、C ++ 11より前のexport
バージョンでは、ヘッダーファイルでテンプレートを宣言して他の場所に実装できるようにするキーワードが標準で定義されていました。
このキーワードを実装した一般的なコンパイラはありません。私が知っているのは、Edison Design Groupによって書かれたフロントエンドだけです。これは、Comeau C ++コンパイラーによって使用されます。コンパイラは適切なインスタンス化のためにテンプレート定義を必要とするため(他の人がすでに指摘したように)、他のすべてのテンプレートではヘッダーファイルにテンプレートを書き込む必要がありました。
その結果、ISO C ++標準委員会はexport
、C ++ 11でテンプレートの機能を削除することを決定しました。
export
に実際に何が私たちに与えられ、何が与えられなかったかを理解しました...そして今、私はEDGの人々に心から同意します:それはほとんどの人々('11含まれています)そう思う、そしてC ++標準はそれなしでより良いです。
標準C ++にはそのような要件はありませんが、一部のコンパイラでは、使用するすべての翻訳単位ですべての関数およびクラステンプレートを使用できるようにする必要があります。実際には、これらのコンパイラでは、テンプレート関数の本体をヘッダーファイルで使用できるようにする必要があります。繰り返します。つまり、これらのコンパイラーは、.cppファイルなどの非ヘッダーファイルでの定義を許可しません。
この問題を軽減することになっているエクスポートキーワードがありますが、移植性にはほど遠いものです。
コンパイラーは、テンプレートパラメーターに指定または推定されたパラメーターに応じて、コードの異なるバージョンをインスタンス化する必要があるため、テンプレートはヘッダーで使用する必要があります。テンプレートはコードを直接表すのではなく、そのコードのいくつかのバージョンのテンプレートであることを忘れないでください。.cpp
ファイル内の非テンプレート関数をコンパイルすると、具体的な関数/クラスがコンパイルされます。これは、異なるタイプでインスタンス化できるテンプレートには当てはまりません。つまり、テンプレートパラメーターを具象型に置き換えるときに具象コードを生成する必要があります。
export
個別のコンパイルに使用するためのキーワードを備えた機能がありました。このexport
機能は非推奨でC++11
あり、AFAIKでは、1つのコンパイラのみがこの機能を実装しています。は使用しないでくださいexport
。分割コンパイルは可能ではないC++
か、C++11
多分にC++17
概念は、それを作る場合、私たちは別のコンパイルのいくつかの方法を持つことができ、。
個別のコンパイルを実現するには、個別のテンプレート本文チェックが可能でなければなりません。コンセプトがあれば解決できるようです。標準委員会で最近発表されたこのペーパーをご覧ください。ユーザーコードでテンプレートコードのコードをインスタンス化する必要があるため、これが唯一の要件ではないと思います。
テンプレートの個別のコンパイルの問題これは、現在作業中のモジュールへの移行で発生している問題でもあると思います。
上記の説明はたくさんありますが、テンプレートをヘッダーと本文に分離する実用的な方法がありません。
私の主な関心事は、定義を変更するときに、すべてのテンプレートユーザーの再コンパイルを回避することです。
すべてのテンプレートのインスタンス化をテンプレート本文に含めることは、私にとって実行可能な解決策ではありません。テンプレートの作成者は、その使用法とテンプレートユーザーがそれを変更する権限を持っていない場合、すべてを知ることができないためです。
私は、古いコンパイラ(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;
}
この方法では、すべてのテンプレートユーザー(および依存関係)ではなく、テンプレートのインスタンス化のみを再コンパイルする必要があります。
MyInstantiatedTemplate.h
ファイルと追加されたMyInstantiatedTemplate
タイプを除いて、私はこのアプローチが好きです。あなたがそれを使わないなら、それは私見で少しきれいです。これを示す別の質問で私の答えを確認してください:stackoverflow.com/a/41292751/4612476
ここで注目すべきものを追加します。テンプレート化されたクラスのメソッドは、関数テンプレートではない場合でも、実装ファイルで問題なく定義できます。
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;
...
}
isEmpty
、それ以外の翻訳ユニットからは呼び出せませんmyQueue.cpp
...
懸念がある場合、追加のコンパイル時間とそれを使用するすべての.cppモジュールの一部として.hをコンパイルすることによって生成されるバイナリサイズの膨張、多くの場合、テンプレートクラスをテンプレート化されていない基本クラスから派生させることができますインターフェイスの型に依存しない部分、およびその基本クラスは.cppファイルに実装できます。
class XBase
必要がある場所に実装しtemplate class X
、型に依存する部分を入れX
、残りをすべて入れXBase
ます。
コンパイラは割り当て用の型を知っている必要があるため、これは正確です。したがって、ヘッダーファイルはc / cppファイルとは異なりコンパイルされないため、テンプレートクラス、関数、列挙型などを、それをパブリックまたはライブラリ(静的または動的)の一部にする場合は、ヘッダーファイルにも実装する必要があります。あります。コンパイラがタイプを知らない場合は、タイプをコンパイルできません。.Netでは、すべてのオブジェクトがObjectクラスから派生しているため、それが可能です。これは.Netではありません。
コンパイラーは、コンパイル・ステップ中にテンプレートを使用すると、テンプレートのインスタンス化ごとにコードを生成します。main.cppに含まれている.hファイルにはまだ実装されていないため、コンパイルおよびリンクプロセスでは、.cppファイルは参照または未定義のシンボルを含む純粋なオブジェクトまたはマシンコードに変換されます。これらは、テンプレートの実装を定義する別のオブジェクトファイルとリンクする準備ができているため、完全なa.out実行可能ファイルがあります。
ただし、テンプレートは、定義するテンプレートのインスタンス化ごとにコードを生成するためにコンパイルステップで処理する必要があるため、ヘッダーファイルとは別にテンプレートをコンパイルするだけでは機能しません。理由は、テンプレートが常に関係しているためです。各テンプレートのインスタンス化は文字通りまったく新しいクラスです。通常のクラスでは、.hと.cppを分離できます。.hはそのクラスの設計図であり、.cppは未加工の実装であるため、実装ファイルを定期的にコンパイルおよびリンクできますが、テンプレート.hを使用すると、クラスはオブジェクトがどのように見えるかではなく、テンプレート.cppファイルはクラスの生の通常の実装ではなく、単なるクラスの青写真であるため、.hテンプレートファイルの実装はすべて
したがって、テンプレートは個別にコンパイルされることはなく、他のソースファイルに具体的なインスタンス化がある場合にのみコンパイルされます。ただし、具体的なインスタンス化では、テンプレートファイルの実装を知っている必要があります。typename T
.hファイルで具象型を使用しても、機能しません。リンクする.cppがあるため、テンプレートは抽象的でコンパイルできないため、後で見つけることができません。実装をすぐに提供して、コンパイルとリンクの対象を理解し、実装を囲んでいるソースファイルにリンクするようにしました。基本的に、テンプレートをインスタンス化する瞬間に、まったく新しいクラスを作成する必要があります。コンパイラに通知しない限り、提供する型を使用するときにそのクラスがどのように見えるかわからない場合は、それを行うことができません。テンプレートの実装なので、コンパイラーはT
自分のタイプに置き換えて、コンパイルしてリンクする準備ができている具象クラスを作成できます。
要約すると、テンプレートはクラスの外観の青写真であり、クラスはオブジェクトの外観の青写真です。コンパイラは具象型のみをコンパイルするため、テンプレートを具体的なインスタンス化とは別にコンパイルすることはできません。つまり、少なくともC ++のテンプレートは純粋な言語の抽象化です。いわばテンプレートの抽象化を解除する必要があります。テンプレートの抽象化が通常のクラスファイルに変換され、通常どおりにコンパイルできるように、それらに処理する具体的な型を与えることによってそうします。テンプレート.hファイルとテンプレート.cppファイルを分離しても意味がありません。.cppと.hだけの分離は、.cppを個別にコンパイルして個別にリンクできる場所だけなので、無意味です。テンプレートは抽象的であるため、個別にコンパイルできないため、テンプレートを使用します。
意味typename T
はリンクのステップではなくコンパイルのステップで置き換えられるのでT
、コンパイラーにとって完全に意味のない具体的な値の型として置き換えられずにテンプレートをコンパイルしようとすると、結果としてオブジェクトコードを作成できません。何を知っていT
ます。
技術的には、template.cppファイルを保存し、他のソースでそれらを見つけたときにタイプを切り替える何らかの機能を作成するexport
ことが可能です。標準には、テンプレートを個別に配置できるようにするキーワードがあると思いますcppファイルですが、実際には多くのコンパイラがこれを実装していません。
余談ですが、テンプレートクラスの特殊化を行う場合、ヘッダーを実装から分離できます。これは、定義による特殊化とは、個別にコンパイルおよびリンクできる具象型に特化しているためです。
個別に実装する方法は次のとおりです。
//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には前方宣言があります。foo.tppには実装があり、inner_foo.hが含まれています。foo.hには、foo.tppを含めるための1行のみが含まれます。
コンパイル時に、foo.hの内容がfoo.tppにコピーされ、ファイル全体がfoo.hにコピーされた後、コンパイルされます。この方法では、制限はなく、1つの追加ファイルと引き換えに、命名は一貫しています。
これは、コードの静的アナライザーが* .tppのクラスの前方宣言を認識しないと壊れるためです。これは、任意のIDEでコードを記述したり、YouCompleteMeなどを使用したりするときに煩わしいものです。
テンプレートのインスタンス化のための「cfront」モデルと「borland」モデルのトレードオフについて説明しているこのgccページをご覧になることをお勧めします。
https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Template-Instantiation.html
「ボーランド」モデルは、作成者の提案に対応し、完全なテンプレート定義を提供し、物事を複数回コンパイルします。
手動および自動のテンプレートのインスタンス化の使用に関する明確な推奨事項が含まれています。たとえば、「-repo」オプションを使用して、インスタンス化する必要のあるテンプレートを収集できます。または、「-fno-implicit-templates」を使用してテンプレートの自動インスタンス化を無効にし、手動でテンプレートをインスタンス化することもできます。
私の経験では、(テンプレートライブラリを使用して)各コンパイルユニットに対してインスタンス化されるC ++標準ライブラリとBoostテンプレートに依存しています。大規模なテンプレートクラスの場合、必要なタイプに対して手動でテンプレートをインスタンス化します。
他のプログラムで使用するためのテンプレートライブラリではなく、実際に動作するプログラムを提供しているため、これが私のアプローチです。この本の作者であるJosuttisは、テンプレートライブラリの作成に多く取り組んでいます。
速度が本当に心配だったら、プリコンパイル済みヘッダーの使用を検討することになります。 ますhttps://gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html
これは多くのコンパイラでサポートを得ています。ただし、テンプレートヘッダーファイルでは、プリコンパイル済みヘッダーは難しいと思います。
ヘッダーファイルに宣言と定義の両方を書き込むことをお勧めするもう1つの理由は、読みやすさのためです。Utility.hにそのようなテンプレート関数があるとします。
template <class T>
T min(T const& one, T const& theOther);
そしてUtility.cppで:
#include "Utility.h"
template <class T>
T min(T const& one, T const& other)
{
return one < other ? one : other;
}
そのため、ここではすべてのTクラスが小なり演算子(<)を実装する必要があります。「<」を実装していない2つのクラスインスタンスを比較すると、コンパイラエラーがスローされます。
したがって、テンプレートの宣言と定義を分離すると、コンパイラーがこれを教えてくれますが、このAPIを独自のクラスで使用するために、ヘッダーファイルを読み取ってこのテンプレートの入出力を確認することだけはできませんオーバーライドする必要のある演算子に関するケース。
テンプレートクラスは、.cppファイルではなく.templateファイル内で実際に定義できます。ヘッダーファイル内でのみ定義できると言っている人は誰でも間違っています。これは、c ++ 98までさかのぼって機能するものです。
コンパイラーが.templateファイルをc ++ファイルとして扱い、インテリセンスを維持することを忘れないでください。
以下は、動的配列クラスの例です。
#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];
}