テンプレートクラスからの単一メソッドのテンプレート特殊化


92

テンプレートクラスを含む次のヘッダーが少なくとも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
なぜインラインを削除したいのですか?審美的に不愉快だと思いますか?コードの意味が変わると思いますか?
マーティンヨーク

1
この方法が「長く」、多くの場所で使用される場合、バイナリコードがどこにでもコピーされるためですよね?私はこれを質問で説明しようとしましたが、それは明確ではなかったと思います... :)
チュイム2009年

@Martin:cppファイルの代わりにこのヘッダーにインクルードする必要のある他の多くのコードが実装に必要な場合はどうなりますか?
sbi 2009年

回答:


71

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

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でチェック


以前は少なくともこれに似たものを試しましたが、他のエラーが発生しましたが、あなたがinlineコピー/貼り付け中にwhile を削除するのを忘れているはずだと言ったので、このように機能しました!
チャイム2009年

同じことが(クラスメソッドではなく)テンプレートフリー関数にも当てはまります。関数の特殊化で同じリンカーエラーが発生しました。関数の特殊化の本文を.cppファイルに移動し、特殊化の宣言をヘッダーに残しましたが、すべてが機能しました。ありがとう!
アルド

私はこの問題に遭遇しました、そして上記は私のためにそれを解決しました。さらに、コンパイラがテンプレートコードを展開する場所にも注意する必要があります。2回実行すると、コンパイラは複数の定義について文句を言います。
Diederik 2013

4

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


3

キーワードをインラインで削除する理由はありません。
いずれにしても、コードの意味は変わりません。


質問のコメントからコピー:この方法が「長く」、多くの場所で使用される場合、バイナリコードがどこにでもコピーされるので、そうですか?私はこれを質問で説明しようとしましたが、それは明確ではなかったと思います... :)
チュイム2009年

1
いいえ。リンカーは余分なコピーを削除します。したがって、アプリケーションまたはlib内では、メソッドのインスタンスは1つしかありません。
マーティンヨーク

3
場合はinline関数内のキーワードの結果は実際に(標準では、コンパイラはヒントとしてそれを取るべきであると述べている)インライン化され、その後、それらの余分なコピーを削除することはできません。ただし、インライン化するためのヒントにすぎません(その主な効果は、「特定の方法でリンクの衝突時にエラーを生成しない」と言うことです)
Yakk-Adam Nevraumont

2

何らかの理由でインラインを削除したい場合、maxim1000の解は完全に有効です。

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

C ++ FAQからの引用

関数がインラインであることを指定するには、いくつかの方法があります。その中には、インラインキーワードが含まれるものとそうでないものがあります。関数をどのようにインラインとして指定しても、それはコンパイラーが無視できるようにする要求です。コンパイラーは、インラインとして指定された関数を呼び出す場所の一部、全部、または一部をインライン展開する場合があります。(それがどうしようもなく漠然と思われる場合でも落胆しないでください。上記の柔軟性は実際に大きな利点です。これにより、コンパイラーは大きな関数を小さな関数とは異なる方法で処理できるようになります。適切なコンパイラオプション。)

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


1

inlineヘッダーファイルに特殊化も残したい場合は、キーワードをそこに保持するのに十分な理由があることを付け加えておきます。

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

リファレンス:https : //stackoverflow.com/a/4445772/1294184


0

これは少しOTですが、他の人を助けるためにここに置いておきたいと思いました。ここで私を導いたテンプレートの専門化についてグーグルで調べていましたが、@ maxim1000の答えは正解であり、最終的に私の問題を解決するのに役立ちましたが、それが十分に明確であるとは思いませんでした。

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.