web-dev-qa-db-ja.com

テンプレートの問題によりリンカーエラーが発生する(C ++)

私はC++テンプレートに関して何が起こっているのかほとんどわかりませんが、特定のプロパティを満たす要素をベクトルで検索する関数を実装しようとしています(この場合、指定された名前の要素を検索しています)。 .hファイルの宣言は次のとおりです。

template <typename T>
T* find_name(std::vector<T*> v, std::string name);

コンパイルすると、関数を呼び出すときにこのリンカーエラーが発生します。

Error   1   error LNK2019: unresolved external symbol "class Item * __cdecl find_name<class Item>(class std::vector<class Item *,class std::allocator<class Item *> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??$find_name@VItem@@@@YAPAVItem@@V?$vector@PAVItem@@V?$allocator@PAVItem@@@std@@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z) referenced in function "public: class Item * __thiscall Place::get_item(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?get_item@Place@@QAEPAVItem@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) place.obj   Program2

繰り返しますが、私はテンプレートが初めてなので、何が起こっているのかわかりません。 Googleを通じてLNK2019を見つけたすべてのインスタンスは、正しいライブラリを使用していませんでしたが、これは私自身の関数であるため、これが発生する理由はわかりません。

また、関連する質問:特定のクラスのサブクラス、つまりテンプレートでなければならないように、テンプレートパラメータを作成する方法はありますか?

42
marsolk

テンプレート定義を呼び出しサイトで利用できるようにする必要があります。つまり.cppファイル。

その理由は、テンプレートをコンパイルできないためです。関数をクッキーと考えてください。コンパイラはオーブンです。

テンプレートは、Cookieの種類を知らないため、単なるCookieカッターです。これは、型が指定されたときに関数を作成する方法をコンパイラーに指示するだけですが、操作される具体的な型がないため、それ自体では使用できません。クッキーカッターは作れません。おいしいクッキーの生地の準備ができている場合にのみ(つまり、コンパイラーに生地[タイプ]が指定されている場合)、クッキーをカットして調理できます。

同様に、特定のタイプのテンプレートを実際に使用する場合にのみ、コンパイラーは実際の関数を生成してコンパイルできます。ただし、テンプレート定義が欠落している場合は、これを行うことができません。関数の呼び出し元がCookieを作成できるように、それをヘッダーファイルに移動する必要があります。

73
GManNickG

おそらく、有効なインスタンス化がないために苦しんでいます。テンプレート定義を別の.cppファイルに配置すると、コンパイラーがそのファイルをコンパイルするときに、必要なインスタンス化が認識されない場合があります。逆に、正しいバージョンのテンプレート関数をインスタンス化する呼び出しサイトでは、関数本体の定義が利用できない場合、コンパイラーは必要な特殊化をインスタンス化するための情報を持っていません。

2つのオプションがあります。関数テンプレートの関数本体をヘッダーファイルに配置します。

例えばヘッダーファイル内:

template <typename T>
inline T* find_name(std::vector<T*> v, std::string name)
{
    // ...
}

または、テンプレートを定義した.cppでテンプレートを明示的にインスタンス化します。

例えばソースファイル内(おそらく#includeingを定義するファイルItem):

template <typename T>
T* find_name(std::vector<T*> v, std::string name)
{
    // ...
}

template Item* find_name<Item>(std::vector<Item*> v, std::string name);
47
CB Bailey

ここでの答えは素晴らしいです。

プロジェクトの.hおよび.cppファイルに加えて、これがよくある理由です。 .inlファイルがよくあります。テンプレート定義は.inlファイルに入れられます。

これらの.inlファイルはインラインを意味し、通常、すべてのヘッダー宣言の後、ファイルの下部にある同じ名前のプレフィックスの.hファイルに含まれます。これにより、ヘッダーファイルの一部になりますが、宣言と定義は分離されます。

それらは美化されたヘッダーファイルなので、通常のヘッダーファイルと同じ予防策を講じる必要があります。つまり、ガードなどを含めます。

11
chollida

同じ問題に遭遇し、これが3つの回避策を示すこれを発見しました: http://www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-ah-file-and -imp

その中には、.cppファイルに「ダミー」メソッドを作成する簡単なものがあります。これは、さまざまなタイプでテンプレート/クラス関数を呼び出します。リンクから貼り付け:

// No need to call this TemporaryFunction() function, it's just to avoid link error.
void TemporaryFunction ()
{
    TestTemp<int> TempObj;
    TestTemp<float> TempObj2;
}
5
KBog

私はあなたが答えられていないように見える2番目の質問があることに気づきました:

特定のクラスのサブクラス、つまりテンプレートでなければならないようにテンプレートパラメータを作成する方法はありますか?

可能です。たとえば、 Boost.TypeTraits_is_base_of_ を参照してください。

しかし、私は興味があります。なぜそれが必要なのですか。通常、そのパラメーターに関するテンプレートの要件は、パラメーターのタイプ自体ではなく、そのタイプを含む式が有効であるものです。たとえば、次のようなものがあるとします。

_template<class T>
void foo(const T& t)
{
    if (t.foo()){
       t.bar("blah");
    }
}
_

Tが次のようなものから継承しなければならないと言う:

_class HasFooAndBar
{
public:
  void foo()const;
  void bar(const char*)const;
};
_

型が操作をサポートしていない場合、関数のインスタンス化はとにかく失敗するため、何ももたらされません。さらに、foo()の適用を不必要に制限します。実際、fooのすべての要件は、t.foo()and t.bar(const char*)がconst Tの有効な式であることです。たとえば、この型はHasFooAndBarから継承されず、依然として有効なfoo()パラメーターです。

_struct DifferentFromHasFooAndBar
{
  bool foo()const;
  std::string bar(const std::string&)const;
};
_
1
Éric Malenfant

テンプレート関数の定義をcppファイルに入れましたか?次に、ヘッダーに移動してインライン化します。

0
dirkgently