[解決済み] テンプレートクラスのコンストラクタの「未定義参照」【重複】について
質問
すべて適切に宣言し、定義しているつもりなのに、なぜこのようなことが起こるのか見当がつきません。
私は以下のようなプログラムをテンプレートで設計しています。これはキューの簡単な実装で、メンバ関数として "add"、 "substract"、 "print"が用意されています。
キュー用のノードは fine "nodo_colaypila.h" で定義しています。
#ifndef NODO_COLAYPILA_H
#define NODO_COLAYPILA_H
#include <iostream>
template <class T> class cola;
template <class T> class nodo_colaypila
{
T elem;
nodo_colaypila<T>* sig;
friend class cola<T>;
public:
nodo_colaypila(T, nodo_colaypila<T>*);
};
次に "nodo_colaypila.cpp" で実装します。
#include "nodo_colaypila.h"
#include <iostream>
template <class T> nodo_colaypila<T>::nodo_colaypila(T a, nodo_colaypila<T>* siguiente = NULL)
{
elem = a;
sig = siguiente;//ctor
}
その後、キュー・テンプレート・クラスとその関数の定義と宣言を行います。
"cola.h"。
#ifndef COLA_H
#define COLA_H
#include "nodo_colaypila.h"
template <class T> class cola
{
nodo_colaypila<T>* ult, pri;
public:
cola<T>();
void anade(T&);
T saca();
void print() const;
virtual ~cola();
};
#endif // COLA_H
"cola.cpp"。
#include "cola.h"
#include "nodo_colaypila.h"
#include <iostream>
using namespace std;
template <class T> cola<T>::cola()
{
pri = NULL;
ult = NULL;//ctor
}
template <class T> void cola<T>::anade(T& valor)
{
nodo_colaypila <T> * nuevo;
if (ult)
{
nuevo = new nodo_colaypila<T> (valor);
ult->sig = nuevo;
ult = nuevo;
}
if (!pri)
{
pri = nuevo;
}
}
template <class T> T cola<T>::saca()
{
nodo_colaypila <T> * aux;
T valor;
aux = pri;
if (!aux)
{
return 0;
}
pri = aux->sig;
valor = aux->elem;
delete aux;
if(!pri)
{
ult = NULL;
}
return valor;
}
template <class T> cola<T>::~cola()
{
while(pri)
{
saca();
}//dtor
}
template <class T> void cola<T>::print() const
{
nodo_colaypila <T> * aux;
aux = pri;
while(aux)
{
cout << aux->elem << endl;
aux = aux->sig;
}
}
そして、これらの機能をテストするプログラムを次のように用意します。
"main.cpp"。
#include <iostream>
#include "cola.h"
#include "nodo_colaypila.h"
using namespace std;
int main()
{
float a, b, c;
string d, e, f;
cola<float> flo;
cola<string> str;
a = 3.14;
b = 2.71;
c = 6.02;
flo.anade(a);
flo.anade(b);
flo.anade(c);
flo.print();
cout << endl;
d = "John";
e = "Mark";
f = "Matthew";
str.anade(d);
str.anade(e);
str.anade(f);
cout << endl;
c = flo.saca();
cout << "First In First Out Float: " << c << endl;
cout << endl;
f = str.saca();
cout << "First In First Out String: " << f << endl;
cout << endl;
flo.print();
cout << endl;
str.print();
cout << "Hello world!" << endl;
return 0;
}
しかし、ビルドすると、コンパイラは、テンプレートクラスのすべてのインスタンスでエラーをスローします。
未定義参照 to `cola(float)::cola()'... (本当は cola'<'float'>'::cola() なのですが、これでは使い物になりません)。
といった具合に。プログラムの中で呼び出されているメンバ関数に対するものも含めると、全部で17の警告が表示されました。
これはなぜでしょうか?これらの関数やコンストラクタは定義されていたのに。コンパイラはテンプレート内の "T" を "float" や "string" などに置き換えることができると思ったのです。
ここのどこかで読んだのですが、各関数の宣言はなぜかヘッダーファイルに書いた方がいいらしいです。そうなのでしょうか?もしそうなら、それはなぜですか?
どのように解決するのですか?
これは、C++プログラミングでよくある質問です。これには2つの有効な答えがあります。どちらの答えにも利点と欠点があり、その選択は文脈に依存することになります。一般的な答えは、ヘッダーファイルにすべての実装を置くことですが、場合によっては、別のアプローチが適しています。選択はあなた次第です。
テンプレート内のコードは、コンパイラが知っている「パターン」に過ぎません。コンパイラはコンストラクタをコンパイルしません。
cola<float>::cola(...)
と
cola<string>::cola(...)
を強制的に実行させるまでです。そして、このコンパイルがコンストラクタである
少なくとも
さもなければ、「未定義参照」エラーが発生します。(の他のメソッドにも適用されます)。
cola<T>
もあります)。
問題の把握
この問題の原因は
main.cpp
と
cola.cpp
は、まず別々にコンパイルされます。で
main.cpp
の場合、コンパイラは
暗黙のうちに
テンプレート・クラスをインスタンス化します。
cola<float>
と
cola<string>
なぜなら、これらの特定のインスタンス化されたものは
main.cpp
. 悪いニュースは、これらのメンバ関数の実装が
main.cpp
に含まれるどのヘッダーファイルにもありません。
main.cpp
そのため、コンパイラはこれらの関数の完全版を
main.o
. をコンパイルするとき
cola.cpp
のインスタンスは暗黙的にも明示的にも存在しないので、コンパイラはそれらのインスタンスをコンパイルしません。
cola<float>
または
cola<string>
. をコンパイルするときに、覚えておいてください。
cola.cpp
このため、コンパイラはどのインスタンス化が必要なのか見当もつきません。
あらゆる
この問題が起きないようにするためです。(
cola<int>
,
cola<char>
,
cola<ostream>
,
cola< cola<int> >
などなど...)
という2つの答えがあります。
-
コンパイラに
cola.cpp
のように、特定のテンプレートクラスが必要となるため、強制的にコンパイルされます。cola<float>
とcola<string>
. -
メンバ関数の実装をインクルードされるヘッダファイルに記述する
あらゆる
他の「翻訳ユニット」(たとえば
main.cpp
がテンプレートクラスを使用します。
回答1:テンプレートの明示的なインスタンス化、およびそのメンバー定義
において
終了
の
cola.cpp
のように、関連するすべてのテンプレートを明示的にインスタンス化する行を追加する必要があります。
template class cola<float>;
template class cola<string>;
の末尾に次の2行を追加します。
nodo_colaypila.cpp
:
template class nodo_colaypila<float>;
template class nodo_colaypila<std :: string>;
これによって、コンパイラがコンパイルする際に
cola.cpp
のすべてのコードを明示的にコンパイルします。
cola<float>
と
cola<string>
クラスがあります。同様に
nodo_colaypila.cpp
の実装が含まれています。
nodo_colaypila<...>
クラスがあります。
このアプローチでは、実装のすべてを1つの
.cpp
ファイル (つまり 1 つの翻訳ユニット) を作成し、明示的なインスタンス化はすべての関数の定義の後 (つまりファイルの最後) に配置するようにします。
回答2:コードを関連するヘッダーファイルにコピーする
一般的な回答は、実装ファイルからすべてのコードを移動することです。
cola.cpp
と
nodo_colaypila.cpp
を
cola.h
と
nodo_colaypila.h
. 長い目で見れば、この方がより柔軟で、余計なインスタンス化(たとえば
cola<char>
を使用することで、より多くの作業を行うことができます。しかし、同じ関数が各翻訳ユニットで何度もコンパイルされることになりかねません。リンカは重複する実装を正しく無視するので、これは大きな問題ではありません。しかし、コンパイルが少し遅くなるかもしれません。
概要
STLや多くの人が書くコードで使われているデフォルトの答えは、すべての実装をヘッダーファイルに置くことです。しかし、よりプライベートなプロジェクトでは、どのテンプレート・クラスがインスタンス化されるかについて、より多くの知識と制御を持つことになります。実際、この「バグ」は機能として捉えられるかもしれません。なぜなら、あなたのコードのユーザが、あなたがテストも計画もしていないインスタンス化を誤って使ってしまうことを防げるからです("私はこれが
cola<float>
と
cola<string>
もし、他のものを使いたい場合は、まず私に言ってください。)
最後に、ご質問のコードには、他に3つの細かい誤字があります。
-
が抜けています。
#endif
nodo_colaypila.h の末尾にある -
cola.hの中で
nodo_colaypila<T>* ult, pri;
はnodo_colaypila<T> *ult, *pri;
- はどちらもポインタです。 -
nodo_colaypila.cpp: デフォルトのパラメータはヘッダファイルにあるはずです
nodo_colaypila.h
この実装ファイルではありません。
関連
-
[解決済み】LLVMで暗黙のうちに削除されたコピーコンストラクタの呼び出し
-
[解決済み】浮動小数点例外エラーが発生する: 8
-
[解決済み】C++ - ステートメントがオーバーロードされた関数のアドレスを解決できない。
-
[解決済み】演算子のオーバーロード C++; <<操作のパラメータが多すぎる
-
[解決済み] C++で、あるコンストラクタを別のコンストラクタから呼び出す(コンストラクタ・チェイニングを行う)ことは可能ですか?
-
[解決済み] テンプレートにおける'typename'と'class'の違い?
-
[解決済み] template "と "typename "キーワードはどこに、なぜ入れなければならないのですか?
-
[解決済み] 未定義の動作とシーケンスポイント
-
[解決済み] ベースクラスのコンストラクタを呼び出す際のルールは?
-
[解決済み] C++テンプレート関数定義の.CPPファイルへの格納
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】coutはstdのメンバではない
-
[解決済み】C++ 非推奨の文字列定数から「char*」への変換について
-
[解決済み] error: 'ostream' does not name a type.
-
[解決済み】致命的なエラー LNK1169: ゲームプログラミングで1つ以上の多重定義されたシンボルが発見された
-
[解決済み】「std::operator」で「operator<<」にマッチするものがない。
-
[解決済み】Visual Studio 2013および2015でC++コンパイラーエラーC2280「削除された関数を参照しようとした」が発生する
-
[解決済み】#include<iostream>は存在するのですが、「識別子 "cout "は未定義です」というエラーが出ます。なぜですか?
-
[解決済み】Enterキーを押して続行する
-
[解決済み】VC++の致命的なエラーLNK1168:書き込みのためにfilename.exeを開くことができません。
-
[解決済み] なぜテンプレートはヘッダーファイルでしか実装できないのですか?