96

テンプレート化されたクラスを含む次のヘッダーが少なくとも2つの.CPPファイルに含まれていることを常に考慮して、このコードは正しくコンパイルされます。

template <class T>
class TClass 
{
public:
  void doSomething(std::vector<T> * v);
};

template <class T>
void TClass<T>::doSomething(std::vector<T> * v) {
  // Do something with a vector of a generic T
}

template <>
inline void TClass<int>::doSomething(std::vector<int> * v) {
  // Do something with a vector of int's
}

ただし、特殊化メソッドのインラインに注意してください。メソッドが複数回定義されているため、リンカーエラー(VS2008ではLNK2005)を回避する必要があります。AFAIKの完全なテンプレートの特殊化は、単純なメソッド定義と同じであるため、これを理解しています。

では、どうすればそれを削除できinlineますか?コードを使用するたびにコードを複製しないでください。私はGoogleを検索し、ここSOでいくつかの質問を読み、提案された解決策の多くを試しましたが、正常に構築されたものはありませんでした(少なくともVS 2008では)。

ありがとう!

4

6 に答える 6

73

単純な関数と同様に、宣言と実装を使用できます。ヘッダー宣言を入力します。

template <>
void TClass<int>::doSomething(std::vector<int> * v);

そして、実装をcppファイルの1つに入れます。

template <>
void TClass<int>::doSomething(std::vector<int> * v) {
 // Do somtehing with a vector of int's
}

インラインを削除することを忘れないでください(私は忘れて、この解決策は機能しないと思いました:))。VC++2005でチェック

于 2009-11-12T16:49:37.320 に答える
4

特殊化定義をCPPファイルに移動する必要があります。関数がテンプレートとして宣言されていない場合でも、テンプレートクラスのメンバー関数の特殊化は許可されます。

于 2009-11-12T16:50:53.590 に答える
3

キーワードをインラインで削除する理由はありません。
とにかくコードの意味を変えることはありません。

于 2009-11-12T16:55:42.203 に答える
2

何らかの理由でインラインを削除したい場合は、maxim1000のソリューションが完全に有効です。

ただし、コメントでは、inlineキーワードは、すべてのコンテンツを含む関数が常にインライン化されることを意味していると思われますが、実際にはコンパイラの最適化に大きく依存しているAFAIKです。

C++FAQからの引用

関数がインラインであることを指定する方法はいくつかあり、そのうちのいくつかはinlineキーワードを含み、他は含まない。関数をインラインとして指定する方法に関係なく、コンパイラーが無視できるようにする要求です。コンパイラーは、インラインとして指定された関数を呼び出す場所の一部、すべて、またはまったくをインライン展開しない場合があります。(それが絶望的に​​曖昧に見える場合でも落胆しないでください。上記の柔軟性は実際には大きな利点です。コンパイラーは大きな関数を小さな関数とは異なる方法で処理できます。さらに、選択するとデバッグしやすいコードをコンパイラーが生成できます。適切なコンパイラオプション。)

したがって、その関数が実際に実行可能ファイルを肥大化させることがわかっている場合、または他の理由でテンプレート定義ヘッダーから削除したい場合を除いて、実際には害を及ぼすことなくそのままにしておくことができます。

于 2015-07-06T10:11:54.057 に答える
1

これは少しOTですが、他の誰かに役立つ場合に備えて、ここに残しておくと思いました。私はここに私を導いたテンプレートの特殊化についてグーグルで調べていました。@maxim1000の答えは正しく、最終的には私の問題を理解するのに役立ちましたが、それが十分に明確であるとは思いませんでした。

私の状況はOPの状況とは少し異なります(しかし、私が思うにこの答えを残すのに十分似ています)。基本的に、私は「ステータスタイプ」を定義するすべての異なる種類のクラスを持つサードパーティライブラリを使用しています。これらのタイプの中心は単純ですenumが、クラスはすべて共通の(抽象的な)親から継承し、演算子のオーバーロードや関数などのさまざまなユーティリティ関数を提供しstatic toString(enum type)ます。それぞれのステータスenumは互いに異なり、無関係です。たとえば、1enumつにはフィールドがありNORMAL, DEGRADED, INOPERABLE、もう1つにはフィールドがありますAVAILBLE, PENDING, MISSING。私のソフトウェアは、さまざまなコンポーネントのさまざまなタイプのステータスの管理を担当しています。toStringこれらの機能を活用したいと思ったのですenumクラスですが、抽象的であるため、直接インスタンス化することはできませんでした。使用したい各クラスを拡張することもできましたが、最終的には、気になる具体的なステータスが何であれtemplate、クラスを作成することにしました。その決定についてはおそらく議論の余地がありますが、それは、各抽象クラスを独自のカスタムクラスで拡張して抽象関数を実装するよりもはるかに少ない作業であると感じました。そしてもちろん、私のコードでは、呼び出して、その文字列表現を出力できるようにしたかっただけです。すべてが完全に無関係だったので、それぞれが独自のものを持っていましたtypenameenumenum.toString(enum type)enumenumtoString(私が学んだいくつかの調査の後で)テンプレートの特殊化を使用して呼び出さなければならなかった関数。それが私をここに導きました。以下は、これを正しく機能させるために私がしなければならなかったことのMCVEです。実際、私のソリューションは@maxim1000のソリューションとは少し異なっていました。

これは、の(非常に単純化された)ヘッダーファイルですenum。実際には、各enumクラスは独自のファイルで定義されていました。このファイルは、私が使用しているライブラリの一部として提供されているヘッダーファイルを表しています。

// file enums.h
#include <string>

class Enum1
{
public:
  enum EnumerationItem
  {
    BEARS1,
    BEARS2,
    BEARS3
  };

  static std::string toString(EnumerationItem e)
  {
    // code for converting e to its string representation,
    // omitted for brevity
  }
};

class Enum2
{
public:
  enum EnumerationItem
  {
    TIGERS1,
    TIGERS2,
    TIGERS3
  };

  static std::string toString(EnumerationItem e)
  {
    // code for converting e to its string representation,
    // omitted for brevity
  }
};

次のファイルを別のコードブロックに分割するためだけにこの行を追加します。

// file TemplateExample.h
#include <string>

template <typename T>
class TemplateExample
{
public:
  TemplateExample(T t);
  virtual ~TemplateExample();

  // this is the function I was most concerned about. Unlike @maxim1000's
  // answer where (s)he declared it outside the class with full template
  // parameters, I was able to keep mine declared in the class just like
  // this
  std::string toString();

private:
  T type_;
};

template <typename T>
TemplateExample<T>::TemplateExample(T t)
  : type_(t)
{

}

template <typename T>
TemplateExample<T>::~TemplateExample()
{

}

次のファイル

// file TemplateExample.cpp
#include <string>

#include "enums.h"
#include "TemplateExample.h"

// for each enum type, I specify a different toString method, and the
// correct one gets called when I call it on that type.
template <>
std::string TemplateExample<Enum1::EnumerationItem>::toString()
{
  return Enum1::toString(type_);
}

template <>
std::string TemplateExample<Enum2::EnumerationItem>::toString()
{
  return Enum2::toString(type_);
}

次のファイル

// and finally, main.cpp
#include <iostream>
#include "TemplateExample.h"
#include "enums.h"

int main()
{
  TemplateExample<Enum1::EnumerationItem> t1(Enum1::EnumerationItem::BEARS1);
  TemplateExample<Enum2::EnumerationItem> t2(Enum2::EnumerationItem::TIGERS3);

  std::cout << t1.toString() << std::endl;
  std::cout << t2.toString() << std::endl;

  return 0;
}

そしてこれは出力します:

BEARS1
TIGERS3

これが私の問題を解決するための理想的な解決策であるかどうかはわかりませんが、それは私にとってはうまくいきました。これで、使用する列挙型の数に関係なく、.cppファイルにメソッドの数行を追加するだけで、自分で実装したり、それぞれを拡張したりすることなくtoString、ライブラリの定義済みメソッドを使用できます。使いたいクラス。toStringenum

于 2019-01-12T02:50:58.027 に答える
1

inlineヘッダーファイルに特殊化も残すつもりなら、キーワードをそこに保持する正当な理由がまだあることを付け加えたいと思います。

「直感的には、何かを完全に特殊化すると、テンプレートパラメータに依存しなくなります。したがって、特殊化をインライン化しない限り、.hではなく.cppファイルに配置する必要があります。そうしないと、違反することになります。 1つの定義ルール...」

参照:https ://stackoverflow.com/a/4445772/1294184

于 2019-11-15T05:49:55.570 に答える