C ++-最近作成されたクラスへの未定義の参照!
-
05-07-2019 - |
質問
この新しいクラスを作成しました:
//------------------------------------------------------------------------------
#ifndef MULTITHREADEDVECTOR_H
#define MULTITHREADEDVECTOR_H
//------------------------------------------------------------------------------
#include <vector>
#include <GL/GLFW.h>
//------------------------------------------------------------------------------
template<class T>
class MultithreadedVector {
public:
MultithreadedVector();
void push_back(T data);
void erase(typename std::vector<T>::iterator it);
std::vector<T> get_container();
private:
std::vector<T> container_;
GLFWmutex th_mutex_;
};
//------------------------------------------------------------------------------
#endif // MULTITHREADEDVECTOR_H_INCLUDED
//------------------------------------------------------------------------------
クラスの定義:
//------------------------------------------------------------------------------
#include "MultithreadedVector.h"
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
template<class T>
MultithreadedVector<T>::MultithreadedVector() {
th_mutex_ = glfwCreateMutex();
}
template<class T>
void MultithreadedVector<T>::push_back(T data) {
glfwLockMutex(th_mutex_);
container_.push_back(data);
glfwUnlockMutex(th_mutex_);
}
template<class T>
void MultithreadedVector<T>::erase(typename vector<T>::iterator it) {
glfwLockMutex(th_mutex_);
container_.erase(it);
glfwUnlockMutex(th_mutex_);
}
template<class T>
vector<T> MultithreadedVector<T>::get_container() {
return container_;
}
問題は、コードで別のクラスの静的メンバーとして使用しようとすると、次のようになることです:
// VehicleManager.h
#ifndef MULTITHREADEDVECTOR_H
#define MULTITHREADEDVECTOR_H
#include "MultithreadedVector.h"
#include "Vehicle.h"
class Foo {
public:
// stuffs
private:
static MultithreadedVector<Vehicle> vehicles_;
...
}
#endif
その後:VehicleManager.cpp
#include "VehicleManager.h"
MultithreadedVector<Vehicle> VehicleManager::vehicles_;
void VehicleManager::Method() {
Vehicle vehicle;
VehicleManager::vehicles_.push_back(vehicle);
}
しかし、それはコンパイルしません:(、私は毎回このエラーメッセージを受け取ります:
C:\***\VehicleManager.cpp|188|undefined reference to `MultithreadedVector<Vehicle>::push_back(Vehicle)'|
なぜ、特にVehicleManager.cppのグローバルスコープで静的クラスメンバーを定義したのか、本当にわかりません。
PS:Code :: Blocksを使用しています。
ありがとう!
解決
これは、クラステンプレートの実装を盲目的にcppファイルに移動したときに起こることです。
それを機能させるには、クラステンプレートをインスタンス化し、cppファイルで指定するTのタイプを正確に知る必要があります。
この場合、これをcppファイルに入れます:
template class MultithreadedVector<Vehicle>;
cppファイルは Vehicle
を認識している必要があることに注意してください。
他のヒント
ほとんどのC ++コンパイラでは、テンプレート宣言とテンプレート定義を分離できません。テンプレートクラスの完全な定義は、.hファイルと.cppファイルに分割するのではなく、単一の.hファイルに配置する必要があります。
テンプレート実装の完全なコードは、テンプレートの使用を確認した時点でコンパイラに表示されるため、テンプレートをインスタンス化し、後でリンクできるオブジェクトコードにコンパイルできます。
テンプレートが宣言されているのと同じヘッダーファイルでテンプレートコードを定義します。
テンプレートの使用方法には2つのケースがあると思います
- 適合タイプの任意のセットに汎用コードを提供する
- 固定されたタイプのセットに汎用コードを提供する
2番目の方法では、テンプレートをヘッダーに配置する必要はありません。テンプレートが使用されているタイプを正確に事前に知っているためです。クラステンプレートのメンバー関数、または別の変換単位で関数テンプレートを、リンカーエラーなしで非常に適切に定義できます。一部の人々は、これが不可能であることを他の人に教え続け、これによって問題を混乱させます。
しかし、宣言と定義を異なる翻訳単位に分離するかどうかは、これら2つのケースに依存すると思います。
-
ケース1を使用する場合は、常にヘッダーに定義を含める必要があります(
.ipp
、.tcc などの特別な名前のファイルに含まれているかどうか) code>または何でも)。この場合でも、宣言から定義を分離することをサポートするC ++フロントエンドは1つしかありません。これは、
export
を実装するintelおよびcomeauコンパイラーで使用されるEDG(edison design group)フロントエンドです。そのキーワードは、一部の人にとっては機能が悪いと見なされており、ほとんどのコンパイラはそれを実装していません。 -
また、ケース2を使用する場合、ヘッダーに定義を配置することは意味がありません。固定セットに必要な関数とクラスを明示的にインスタンス化するだけです。タイプの。この2番目のシナリオでは、テンプレートを使用して、冗長なコードを維持するための負担を軽減し、場合によっては特定のタイプに特別な動作を導入するオプションを開きます。
シナリオは明らかにケース1の状況であるため、ヘッダーに定義を追加する必要があります。
VehicleManager.hでコピーと貼り付けのエラーが発生しているようです:
#ifndef MULTITHREADEDVECTOR_H
#define MULTITHREADEDVECTOR_H
これにより、クラスFooが定義されているヘッダーファイルを含めることが禁止されます。
ほとんどの場合、cppファイルにテンプレート実装を書き込むことはできません。コンパイラが適切にインスタンス化できるように、テンプレート実装をヘッダーファイルに移動します。
他の回答とは別に:
get_container()
もガードする必要があります。このメソッドは基本的に container _
要素をコピーするため、保護する必要があります。
template<class T>
vector<T> MultithreadedVector<T>::get_container()
{
return container_;
}
ある意味でのテンプレートは、一種の高度で特殊なマクロです。あなたがしようとしているように見えるように、実際にテンプレート定義を個別にコンパイルすることはできません。
ほとんどの人にとって、これは彼らが宣言からteplate定義を分離することさえ気にしないことを意味します。 これをさらに一歩進め、分離を拒否する同僚が1人いますem>非テンプレートの定義と宣言も。
宣言からテンプレートの定義を配置する場合、いくつかのオプションがあります。
最初のオプションは、C ++を使用することです&quot; export&quot;キーワード。この一見シンプルなソリューションの問題は、誰もサポートしていないことです。 。コンパイラの作成者が実装するのは非常に困難です。
2番目のオプションは、 3番目のタイプのファイルを使用することです、多くの場合&quot; .ipp&quot ;、宣言用。ここでのコツは、まだ「#included」する必要があるファイルですが、別のファイルであるため、「。cpp」に含める必要があるだけです。ファイル。 &quot; .ipp&quot;を#includeすると、リンカが気に入らないことがわかりました。複数の&quot; .cpp&quot;で単一のプログラムでファイルを作成するため、インクルーダーとして1つを選択する必要があります。