13

私の同僚は、彼がチームで使用した小さなデザインについて話してくれました。これは、非常に分離された方法で特化できる一種の特性クラスです。

それがどのように機能するのかを理解するのに苦労しました。また、自分のアイデアがまだよくわからないので、ここで助けを求めようと思いました.

ここでは g++、具体的にはバージョン 3.4.2 と 4.3.2 (両方で動作するようです) について話しています。

アイデアは非常に単純です。

1- インターフェイスを定義する

// interface.h
template <class T>
struct Interface
{
  void foo(); // the method is not implemented, it could not work if it was
};

//
// I do not think it is necessary
// but they prefer free-standing methods with templates
// because of the automatic argument deduction
//
template <class T>
void foo(Interface<T>& interface) { interface.foo(); }

2-クラスを定義し、ソースファイルでこのクラスのインターフェースを特殊化します(そのメソッドを定義します)

// special.h

class Special {};


// special.cpp

#include "interface.h"
#include "special.h"

// 
// Note that this specialization is not visible outside of this translation unit
//
template <>
struct Interface<Special>
{
  void foo() { std::cout << "Special" << std::endl; }
};

3- 使い方も簡単です:

// main.cpp

#include "interface.h"

class Special; // yes, it only costs a forward declaration
               // which helps much in term of dependencies

int main(int argc, char* argv[])
{
  Interface<Special> special;
  foo(special);
  return 0;
};

Interfaceforの特殊化を定義した翻訳単位がない場合、これは未定義のシンボルですSpecial

さて、これにはキーワードが必要だと思っていたでしょうがexport、これは私の知る限り、g ++で実装されたことはありません(C ++コンパイラで実装されたのは1回だけで、作成者は時間と労力を考えるとそうしないようにアドバイスしています)。

テンプレートメソッドを解決するリンカーと関係があると思われます...

  • このようなものに会ったことがありますか?
  • それは標準に準拠していますか、それともうまくいくのは幸運な偶然だと思いますか?

私はその構成にかなり戸惑っていることを認めなければなりません...

4

2 に答える 2

10

@Stewardが疑ったように、それは有効ではありません。正式には、未定義の動作を効果的に引き起こしています。これは、違反に対して診断が不要であるという標準の規則があり、実装が必要なことを黙って実行できることを意味します。で14.7.3/6

テンプレート、メンバー テンプレート、またはクラス テンプレートのメンバーが明示的に特殊化されている場合、その特殊化は、そのような使用が発生するすべての翻訳単位で、暗黙的なインスタンス化を発生させる特殊化の最初の使用の前に宣言する必要があります。 ; 診断は必要ありません。

実際には、少なくとも GCC ではInterface<T>、特殊化が宣言されておらず、mainでは表示されないため、暗黙的にプライマリ テンプレートをインスタンス化してから を呼び出しInterface<T>::fooます。その定義が表示されている場合は、メンバー関数のプライマリ定義を開始します (これが、定義されている場合に機能しない理由です)。

インスタンス化された関数名シンボルは、異なるオブジェクト ファイルに複数回存在する可能性があり、最終的なプログラムで 1 つのシンボルにマージする必要があるため、リンケージが弱いです。反対に、テンプレートではない明示的な特殊化のメンバーは強いリンケージを持っているため、弱いリンケージ シンボルを支配し、呼び出しを特殊化で終わらせます。これはすべて実装の詳細であり、標準には弱い/強いリンケージの概念はありません。specialオブジェクトを作成する前に、特殊化を宣言する必要があります。

template <>
struct Interface<Special>;

標準はそれをむき出しにします(私が強調します)

関数テンプレート、クラス テンプレート、クラス テンプレートのメンバー関数、クラス テンプレートの静的データ メンバー、クラス テンプレートのメンバー クラス、クラス テンプレートのメンバー クラス テンプレート、クラス テンプレートのメンバー関数テンプレート、メンバーのメンバー関数の明示的な特殊化宣言の配置クラス テンプレートのテンプレート、非テンプレート クラスのメンバー テンプレートのメンバー関数、クラス テンプレートのメンバー クラスのメンバー関数テンプレートなど、およびクラス テンプレートの部分特殊化宣言の配置、非テンプレート クラスのメンバー クラス テンプレート、メンバークラステンプレートのクラステンプレートなど、上と下で指定されているように、明示的な特殊化宣言の相対的な位置付けと翻訳単位でのそれらのインスタンス化のポイントに従って、プログラムが整形式であるかどうかに影響を与える可能性があります。専門化を記述するときは、その場所に注意してください。またはそれをコンパイルすることは、その自己犠牲を燃やすような試練になるでしょう.

于 2010-05-09T13:20:14.837 に答える
5

それはかなりきれいです。ただし、どこでも機能することが保証されているかどうかはわかりません。彼らがやっていることは、故意に定義されていないテンプレートメソッドを持ち、独自の翻訳単位に隠れた特殊化を定義しているようです. それらは、元のクラス テンプレート メソッドと特殊化の両方に同じ名前マングリングを使用するコンパイラに依存しています。これはおそらく非標準だと思います。次に、リンカーはクラス テンプレートのメソッドを探しますが、代わりに特殊化を見つけます。

ただし、これにはいくつかのリスクがあります。たとえば、リンカでさえ、誰もメソッドの複数の実装を取得しません。テンプレート メソッドは selectany とマークされます。これは、テンプレートがインラインであることを意味するためです。リンカが複数のインスタンスを検出した場合、エラーを発行する代わりに、最も便利なインスタンスを選択します。

残念ながら、それが機能するのは幸運な偶然のようです.

于 2010-05-09T12:01:45.647 に答える