1

この記事では、彼らは (c) (b) の明示的な特殊化と言っています。私の疑問は、それが (a) の明示的な特殊化であると言えないのはなぜですか? テンプレートを特定のタイプに特化できるからです。したがって、 int* の特殊化中に、 (c) (b) の明示的な特殊化と言う理由。

template<class T>   // (a) a base template 
void f( T );

template<class T>   // (b) a second base template, overloads (a) 
void f( T* );       //     (function templates can't be partially 
                //     specialized; they overload instead)

template<>          // (c) explicit specialization of (b) 
void f<>(int*);

コメントは、物事を理解するのに役立ちます。

4

1 に答える 1

3

(b) が存在しない場合、(c) は確かに (a) の有効な特殊化になります。実際、コンパイラが (b) を認識する前に (c) が現れるようにソース行の順序を変更するだけで、(a) の特殊化になります!

次のコードを検討してください。

int main()
{
    int a;
    f(&a);
    return 0;
}

ここで、コンパイラの立場になってみましょう。f引数を持つ一致する関数を見つける必要がありint*ます。職業はなんですか?

  • 最初に、既知のすべての非テンプレート関数を試してf、引数の型 (この場合は ) に一致する関数があるかどうかを確認しますint*
  • 完全に一致するものが見つからない場合は、知っているすべての基本テンプレートを調べます。この場合、 と の 2 つがf<T>ありf<T*>ます。クラスとは異なり、関数テンプレートは部分的に特殊化できないため、コンパイラに関する限り、これらは完全に別個のオーバーロードであることに注意してください。
  • 明らかにf<T*>ベース テンプレートの方が適しているため、 でインスタンス化しますT=int。の特殊化をすでに見た場合はf<int*>、それを使用し、それ以外の場合は関数を生成します。

ここに面白いことがあります。元のコードの順序を変更すると、

template<class T> void f( T ); // (i)

template<> void f<>(int*); // (ii)

template<class T> void f( T* ); // (iii)

次に、コンパイラは (ii) を (i) の特殊化と見なします。これは、物事を順番に処理し、(ii) に到達した時点で (iii) がまだ存在することを認識していないためです。しかし、これはベース テンプレートでのみ一致するため、(iii) は (i) よりも適切であると判断し、現在 (iii) には特殊化がないため、デフォルトのインスタンス化が取得されます。

これらはすべて非常にややこしく、経験豊富な C++ プログラマーでさえつまずくことがあります。したがって、基本的なルールは次のとおりです。関数テンプレートを特殊化せず、代わりに通常の関数のオーバーロードを使用します。通常の古い非テンプレート

void f(int*);

何よりも先に一致し、この混乱全体を回避します。


編集: nm は、コメントで標準への参照を要求しました。残念ながら手元にあるのは C++03 バージョンだけですが、次のようになります。

パラグラフ 4.7.3.3: 「明示的に特殊化されている関数テンプレートまたはクラス テンプレートの宣言は、明示的な特殊化の宣言の時点でスコープ内にあるものとします。」.

そのため、上記の例では、(iii) がまだスコープに含まれていないため、(ii) を (iii) の明示的な特殊化と見なすことはできません。

セクション 4.8.3: 「その [関数] 名への呼び出しが書き込まれると... テンプレート引数の推定 (14.8.2) と明示的なテンプレート引数のチェック (14.3) が各関数テンプレートに対して実行され、テンプレート引数の値が検出されます。 (存在する場合) その関数テンプレートで使用して、呼び出し引数で呼び出すことができる関数テンプレートの特殊化をインスタンス化できます。"

言い換えれば、(私が読んだ限りでは) どのような場合でも、常に各基本テンプレートを調べます。明示的な特殊化を提供しても違いはありません。

同じ段落が続きます: 「各関数テンプレートについて、引数の推定とチェックが成功した場合、テンプレート引数 (推定および/または明示的) を使用して、単一の関数テンプレートの特殊化をインスタンス化します。これは、候補関数セットに追加されます。オーバーロードの解決に使用されます。

したがって、明示的な特殊化が考慮されるのは、この時点 (私が読んだとき) だけです。

最後に、おそらくこの場合最も重要なのは、セクション 13.3.3 で、オーバーロード セット内の「実行可能な最適な関数」を選択することです。関連する項目は次の 2 つです。

  • 次の場合、F1 は F2 より優れています。これが、一致しようとしたときにバージョンf<T*>がバージョンよりも先に選択される理由です。これは「より特殊な」テンプレートであるためです。f<T>f(int*)

  • 「F1 は非テンプレート関数であり、F2 は関数テンプレートの特殊化である」場合、F1 は F2 よりも優れています。これは、元の回答の最後にある私のアドバイスの基礎でした。

ふぅ!

于 2013-10-25T06:02:09.430 に答える