9

説明も説明も見つからなかった興味深い点に出くわしました。次のテンプレート定義(mingw g ++ 4.6.2でコンパイル)を検討してください。

template <typename T, typename S>
class Foo
{
public:
    void f(){}
    void g(){}
};

必要に応じて、単一メンバー関数を完全に特殊化できます。

template <>
void Foo<char,int>::f() {}

しかし、部分的な特殊化は「不完全な型の無効な使用'クラスFoo<...>'」エラーで失敗します。

template <typename T, typename S>
void Foo<T,S*>::f()
{
}

template <typename T>
void Foo<T,int>::f()
{
}

そして、その理由がわかりません。予測できない問題を回避するために、意識的に設計を決定したのでしょうか。見落としですか?

4

3 に答える 3

7

部分的な特殊化の概念は、クラステンプレート(§14.5.5で説明)とメンバーテンプレート(つまり、§14.5.5.3/ 2で説明されている、それ自体がテンプレート関数であるテンプレートクラスのメンバー)にのみ存在します。これは、クラステンプレートの通常のメンバーには存在せず、関数テンプレートにも存在しません。これは、標準で説明されていないためです。

ここで、メンバー関数の部分的な特殊化の定義を与えることによって、次のように主張するかもしれません。

template <typename T>
void Foo<T,int>::f()
{ }

クラステンプレートの部分的な特殊化を暗黙的Foo<T,int>に定義します。ただし、これ標準によって明示的に除外されています。

(§14.5.5/ 2)各クラステンプレートの部分的特殊化は別個のテンプレートであり、テンプレートの部分的特殊化(14.5.5.3)のメンバーに定義を提供する必要があります。

(§14.5.5.3/ 1)[...]クラステンプレートの部分特殊化のメンバーは、プライマリテンプレートのメンバーとは無関係です。定義を必要とする方法で使用されるクラステンプレートの部分特殊化メンバーを定義する必要があります。プライマリテンプレートのメンバーの定義は、クラステンプレートの部分的な特殊化のメンバーの定義として使用されることはありません。[...]

後者は、そのメンバーの1つを定義するだけでは、部分的な特殊化を暗黙的に定義することは不可能であることを意味します。そのメンバーの存在自体は、プライマリテンプレートの定義に従わないため、メンバーを定義することと同じです。宣言されておらず、許可されていない関数(非テンプレートクラスでも)。

一方、クラステンプレートのメンバー関数には、明示的な特殊化(または、いわゆる完全な特殊化)の概念が存在します。これは、標準によって明示的に記述されています。

(§14.7.3/ 1)次のいずれかの明示的な特殊化:
[...]
—クラステンプレートのメンバー関数
[...]
は、template<>によって導入された宣言によって宣言できます。[...]

§14.7.3/14は詳細を説明しています:

(§14.7.3/ 14)クラステンプレートのメンバーまたはメンバーテンプレートは、メンバーまたはメンバーテンプレートがクラステンプレート定義で定義されている場合でも、クラステンプレートの特定の暗黙的なインスタンス化に明示的に特化できます。[...]

したがって、メンバーの明示的な特殊化の場合、クラステンプレートの残りのインスタンス化は暗黙的に機能します。これは、プライマリテンプレート定義、または定義されている場合は部分的な特殊化から派生します。

于 2012-12-18T02:02:57.363 に答える
5

標準から簡潔な引用を見つけようとしましたが、それはないと思います。実際には、テンプレート関数(または、さらに言えば、テンプレートエイリアス)の部分的な特殊化などはありません。クラステンプレートのみが部分的な特殊化を持つことができます。

テンプレートについては少し忘れましょう。C ++では、クラス名と関数名に大きな違いがあります。特定のスコープ内に存在できるクラスの定義は1つだけです。(さまざまな宣言を行うことができますが、それらはすべてOne True Classを参照します。)したがって、名前は実際にクラスを識別します。

一方、関数名は一種のグループIDです。スコープ内でまったく同じ名前の関数をいくつでも定義できます。関数名を使用して関数を呼び出す場合、コンパイラーは、さまざまな可能性を調べ、それぞれのシグニチャーを指定された引数と照合することにより、実際にどの関数を意味しているかを把握する必要があります。名前を共有するさまざまな機能の間に関係はありません。それらは完全に別個のエンティティです。

だから、大したことはありません。あなたはこれをすべて知っていましたね?しかし、テンプレートに戻りましょう。

テンプレート化されたクラスの名前はまだ一意です。部分的な特殊化を定義することはできますが、同じテンプレートクラスを明示的に特殊化する必要があります。このメカニズムは、表面的には上記の関数名解決アルゴリズムに似ていますが、大きな違いがあります。その1つは、関数プロトタイプとは異なり、同じスコープ内に異なる種類のテンプレートパラメーターを持つ2つのクラステンプレートを使用できないことです。

一方、テンプレート化された関数は、一意の名前を定義する必要はありません。テンプレートは、通常の機能のオーバーロードメカニズムに置き換わるものではありません。したがって、コンパイラが関数名の意味を理解しようとするときは、その関数名のすべてのテンプレート化された宣言とテンプレート化されていない宣言を考慮し、テンプレート化された宣言を一連のテンプレートパラメータ割り当てに解決してから(可能であれば)、一度それを実行する必要があります。可能な関数オブジェクトのリストがあります。通常の過負荷解決で最適なものを選択してください。

これは、テンプレート化されたクラステンプレートパラメータの解決とはまったく異なるアルゴリズムです。提供されたテンプレート引数のリストを宣言されたテンプレートパラメータのリストと照合するだけでなく、クラステンプレートを解決する方法であるため、一致する可能性のある各テンプレート関数を取得する必要があります(たとえば、少なくとも適切な数のパラメータがあります)。 ; 提供された引数をテンプレートと統合することにより、テンプレートパラメータを推測します。次に、オーバーロード解決のさらなるラウンドのために、オーバーロードセットに解決スペシャライゼーションを追加します。

そのプロセスに部分的な特殊化の解決策を追加することも可能だったと思いますが、部分的な特殊化と関数のオーバーロードの間の相互作用は、疑似魔法の動作につながる可能性が高いと思います。イベントでは、それは必要ではなかったので、そのようなメカニズムはありません。(関数テンプレートを完全に特殊化できます。完全に特殊化すると、推測するテンプレート引数がないため、問題はありません。)

これがスクープです。テンプレート化された関数を部分的に特殊化することはできませんが、同じ名前の関数テンプレートをいくつでも提供することを妨げるものは何もありません。それらはすべて過負荷解決で考慮され、通常どおり、最良のものが勝ちます。

通常、それは実際にはオーバーロードのニーズには十分です。テンプレート化された関数については、通常の関数と同じように考える必要があります。提供された引数に基づいて、必要な関数を選択する方法を考えてください。推論するのではなく、関数呼び出しでテンプレートパラメータを指定する必要があると思われる場合は、関数をテンプレート化されたクラスの(おそらく静的な)メンバーにして、テンプレート引数をクラスに指定します。

お役に立てば幸いです...

于 2012-12-18T01:13:26.707 に答える
3

f違いは、最初の(有効な)明示的な特殊化を行うとき:の違いだと思います。

template <>
void Foo<char,int>::f() {}

の暗黙的なインスタンス化を行っていますFoo<char,int>。しかし、部分的な特殊化を試してみると、次のようになります。

template <typename T>
void Foo<T,int>::f()
{
}

コンパイラーは、特殊化を行う前に暗黙的にインスタンス化する必要がありますFoo<T,int>が、が原因でそれを行うことはできませんT。そしてそれは失敗します。

次のコードでそれを確認できます。

template <typename T, typename S>
class Foo
{
public:
    void f(){}
    void g(){}
};


template <>
void Foo<char,int>::f() //line 11
{}

template <>
class Foo<char,int> //line 15
{};

それでg++エラーが発生します:

test.cpp:15:7: error: specialization of ‘Foo<char, int>’ after instantiation
test.cpp:15:7: error: redefinition of ‘class Foo<char, int>’
test.cpp:2:7: error: previous definition of ‘class Foo<char, int>’

Withclang++は少し明確です:

test.cpp:15:7: error: explicit specialization of 'Foo<char, int>' after instantiation
class Foo<char,int>
      ^~~~~~~~~~~~~
test.cpp:11:6: note: implicit instantiation first required here
void Foo<char,int>::f() 
     ^
于 2012-12-17T23:09:59.507 に答える