スローキャッチがリンクエラーを引き起こす
-
08-07-2019 - |
質問
次のタイプのリンケージエラーが発生しています:
Festival.obj:エラーLNK2019: 未解決の外部シンボル<!> quot; public: void __thiscall Tree :: add(class Price <!> amp;)<!> quot; (?add @?$ Tree @ VPrice @@@@ QAEXAAVPrice @@@ Z) 関数で参照される __catch $?AddBand @ Festival @@ QAE?AW4StatusType @@ HHH @ Z $ 0
以前はtry-catchメカニズムに関係していると考えていましたが、それ以降はそうではないと言われました。これは質問の更新版です。
Visual Studio 2008を使用していますが、g ++でも同様の問題があります。
関連するコード:
Festival.cppで
#include "Tree.h"
#include <exception>
using namespace std;
class Band{
public:
Band(int bandID, int price, int votes=0): bandID(bandID), price(price), votes(votes){};
...
private:
...
};
class Festival{
public:
Festival(int budget): budget(budget), minPrice(0), maxNeededBudget(0), priceOffset(0), bandCounter(0){};
~Festival();
StatusType AddBand(int bandID, int price, int votes=0);
...
private:
Tree<Band> bandTree;
...
};
StatusType Festival::AddBand(int bandID, int price, int votes){
if ((price<0)||(bandID<0)){
return INVALID_INPUT;
}
Band* newBand=NULL;
try{
newBand=new Band(bandID,price-priceOffset,votes);
}
catch(bad_alloc&){return ALLOCATION_ERROR;}
if (bandTree.find(*newBand)!=NULL){
delete newBand;
return FAILURE;
}
bandTree.add(*newBand);
....
}
Tree.hで:
template<class T>
class Tree{
public:
Tree(T* initialData=NULL, Tree<T>* initialFather=NULL);
void add(T& newData);
....
private:
....
};
興味深いことに、T型がintのようなプリミティブ型であるときにTree関数を使用しようとしても、リンクエラーは発生しません。
解決
Tree.cppはありますか?ある場合は、リンクするのを忘れた可能性がありますか? Tree :: addの実装はどこですか? さらに、Tree :: addを呼び出す場所がわかりません。新しいステートメントの直後のtryステートメント内にあるべきでしょうか?
リマインダー:
ほとんどのコンパイラ(つまり、個別のコンパイルを実行するコンパイラ)では、テンプレートクラスを使用するソースファイルのコンパイル中に、テンプレートクラスのメンバー関数の実装を表示する必要があります。通常、人々はこのルールに従い、メンバー関数の実装をヘッダーファイル内に配置します。
Maybe Tree :: addはヘッダー内にありませんか?議論された場合の可能な解決策は、ヘッダーファイル内にTree :: add実装を配置することです。
テンプレートクラスは<!> quot; real <!> quot;ではないため、通常のクラスとテンプレートクラスの違いが存在します。クラス-それはまあ、テンプレートです。 Treeクラスを通常のクラスとして定義している場合、コンパイラはすぐにコードを使用できます。テンプレートの場合、コンパイラは最初に<!> quot; writes <!> quot;実際のクラスでは、テンプレートパラメータを指定したタイプに置き換えます。現在、コンパイラはcppファイルを1つずつコンパイルします。彼は他のcppファイルを認識しておらず、他のcppファイルからは何も使用できません。 Tree:addの実装が次のようになっているとしましょう:
void Tree::add(T& newData)
{
newData.destroyEverything();
}
TがdestroyEverythingメソッドを持っている限り、完全に正当です。コンパイラがClass.cppをコンパイルするとき、知らないことをTで行わないようにしたいのです。たとえば、intにはdestroyEverythingがないため、Tree<int>
は機能しません。コンパイラは、Tではなくintを使用してコードを記述しようとし、コードがコンパイルされないことを検出します。しかし、コンパイラは<!> quot; sees <!> quot;現在のcppとそれに含まれるすべてのもののみ、それは別のcppにあるため、追加機能を検証できません。
問題はありません
void Tree::add(int& newData)
{
newData.destroyEverything();
}
別のcppで実装されているのは、コンパイラがintのみが受け入れ可能なタイプであり、<!> quot;自分自身に頼る<!> quot; Tree.cppをコンパイルしようとすると、エラーが見つかります。
他のヒント
try
/ catch
には何か関係があるのですか? Tree::add(class Price &)
と<=>の行をコメントアウトし、残りのコードをそのままにしてビルドするとどうなりますか?
リンク行から<=>を定義するライブラリが欠落しているだけかもしれません。
更新:プリミティブ型でツリー関数を使用しても、リンクエラーは発生しません。 言われたことのいくつかに照らして質問を更新しました。
他の人が述べたように、Treee :: add()の実装を示し、どのようにリンクしているのか教えてください。
無関係な点で、次のような構造を使用している場合:
Band* newBand=NULL;
try{
newBand=new Band(bandID,price-priceOffset,votes);
}
catch(bad_alloc&){return ALLOCATION_ERROR;}
コード全体で、率直に時間を無駄にしています。最新のOSでメモリが枯渇する可能性はほとんどなく、発生した後に何か役に立つことをする可能性はほぼゼロです。単に次のように言った方がはるかに良いでしょう:
Band * newBand = new Band ( bandID, price - priceOffset, votes );
おそらく:
Band newBand( bandID, price - priceOffset, votes );
この場合、例外処理を忘れます。
コメントに書いた:
これを検討しましたが、関数はTree.hの一部であり、それを含めています。定義されている関数は次のとおりです。template void Tree :: add(T <!> amp; newData);次のように呼び出します:priceTree.add(* newPriceNode);一方、priceTreeはTreeであり、どちらも問題のcppファイルで定義されています。
代わりに:
priceTree.add(*newPriceNode);
試用:
priceTree.add(newPriceNode); //no "*" before "newPriceNode"
add()は、ノードへのポインタではなく、ノードへの参照を取ります(ツリーの定義に従って)。
コンパイラエラーではなく、リンケージエラーが発生しています。これは、コンパイラーが関数Tree::add()
の種類を知っていたが、定義を持っていなかったことを示しています。 Tree.hでは、add()関数の宣言が表示されますが、定義は表示されません。私には奇妙に見えます。 Tree.hがどこから来たのか誰にもわかりますか?
通常、テンプレートクラスにはインクルードファイルにメンバー関数定義が含まれています。関数はどこかでインスタンス化する必要があるため、最も簡単なことはコンパイラーが使用時にインスタンス化してリンカーにソートさせることです。定義がTree.hにある場合、すべてが計画どおりに機能することを期待しています。
だから、手足に出て、定義がリンクされていない別のファイルにあり、Tree<int>
のような基本型をインスタンス化するための他の場所があることを提案します。これは通常、コンパイルを効率化するためです。通常、これらのものは複数の場所でコンパイルされ、時間がかかります。
その場合に必要なことは、Tree<Band>
がインスタンス化される場所を見つけ、クラスのインスタンス化を追加することです。
ここから離れることもできますが、説明はあなたが与えた事実に当てはまります。
最初のコメントの後に編集:
テンプレートは通常の関数よりもやや複雑であり、通常は実際の問題ではありません。すべての呼び出しの定義がTree.hにある場合、Festival.cppはTree<Band>::add()
をインスタンス化でき、すべてがクールになります。これは通常の手法であり、使用していないためにこの問題に直面しています。
関数を作成すると、その関数はコンパイルされ、リンカーはそれを見つけます。その関数を呼び出すルーチンは、関数プロトタイプを知る必要があるため、どのように呼び出すかがわかります。テンプレートを作成するとき、プログラムに直接移動するものは何も作成しませんが、テンプレートを使用すると、すべての関数を作成するものとしてカウントされます。
したがって、Tree<T>::add
関数をコンパイルするには、プログラムのどこかにTree<T>
を使用する必要があります。 Band
のインスタンス化時にコンパイラーが<=>の定義を使用できるようにする必要があります。そうでない場合、コンパイラーは何をコンパイルするかわからないからです。この場合、関数呼び出しが生成され、関数が他の場所でコンパイルされていることを確認できます。
したがって、<=>と<=>の両方の定義にアクセスできるファイル内で<=>をインスタンス化する必要があります。これはおそらく、Tree.cppまたはFestival.hを含む、または含むファイルを意味します。
リンカはすでにTree.cppを使用していますが、Tree.cppには<=>が定義されていないため、リンカにとって意味がありません。テンプレートはコンパイラにとってのみ有用であり、リンカはコンパイラがテンプレートから生成したものに対してのみ動作します。
これを解決する簡単な方法は、Tree.cppから定義を取得してTree.hに配置することです。残念ながら、コンパイルとリンクの時間が長くなる可能性があります。もう1つの方法は、Tree.cppで使用するすべてのテンプレートをインスタンス化し、そこでコンパイルされるようにすることです。