SO に関する別の質問に回答しているときに、gcc でやや疑わしいコンパイラ エラーに遭遇しました。問題のあるスニペットは
template <class T> class A;
template <class T, class U>
void operator*(A<T>, A<U>);
template <class T>
class A {
friend void ::operator*(A<T>, A<T>);
...
その最後の行は有名な警告を与えます
フレンド宣言 '
void operator*(A<T>, A<T>)
' は非テンプレート関数を宣言しています
後でハードエラーにつながります。完全なコードはここにあります。
さて、問題は、その振る舞いが適切ではないと思うことです。[temp.friend]/1 の標準は次のように述べています。
テンプレート宣言ではないフレンド関数宣言の場合:
— フレンドの名前が修飾または非修飾の template-id である場合、フレンド宣言は関数テンプレートの特殊化を参照します。
— フレンドの名前が修飾 ID であり、一致する非テンプレート関数が指定されたクラスまたは名前空間で見つかった場合、フレンド宣言はその関数を参照します。それ以外の場合は、
— フレンドの名前が修飾 ID であり、一致するテンプレート関数の特殊化が 指定されたクラスまたは名前空間で見つかった場合、フレンド宣言はその関数の特殊化を参照します。それ以外の場合は、
これは C++03 です。C++11 には同様の節が含まれています
テンプレートの特殊化は、[temp.spec]/4 によって定義されます。
... 特殊化は、インスタンス化または明示的に特殊化されたクラス、関数、またはクラス メンバーです (14.7.3)。
および [temp.fct.spec]/1:
関数テンプレートからインスタンス化された関数は、関数テンプレートの特殊化と呼ばれます。関数テンプレートの明示的な特殊化も同様です。テンプレート引数は明示的に指定できます...
[temp.arg.explicit]/2 は、関数仕様のテンプレート引数リストの指定について次のように述べています。
関数テンプレートの特殊化を参照するときに、テンプレート引数リストを指定できます
...
— フレンド宣言で。
推定できる末尾のテンプレート引数 (14.8.2) は、明示的なテンプレート引数のリストから省略できます。すべてのテンプレート引数を推測できる場合は、それらをすべて省略できます。この場合、空のテンプレート引数リスト <> 自体も省略できます。
したがって、[temp.fct.spec]/1::operator*<T,T>(A<T>, A<T>)
は関数テンプレートの特殊化です。テンプレートパラメータは推定できるので、 として参照できます::operator*(A<T>, A<T>)
。したがって、フレンド宣言の修飾 ID は、関数テンプレートの特殊化を示していると結論付けます。
強調された条件は満たされていると思います。したがって、フレンド宣言は、オペレーター テンプレート (暗黙の) 特殊化を使用してクラスとフレンドになる必要があります。しかし、gcc はそうではないと考え、4 番目の箇条書きに進みます。これは、修飾された ID によって実際に名前が付けられているにもかかわらず、修飾されていない ID によって指定された友人にのみ関係します。
この場合、私の解釈は正しいですか、それともgccは正しいですか?