5

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は正しいですか?

4

1 に答える 1

0

私はgccが正しいと信じています。

まず現在の文言:

フレンドの名前が修飾 ID であり、一致する関数テンプレートが指定されたクラスまたは名前空間で見つかった場合、フレンド宣言はその関数テンプレートの推定特殊化(14.8.2.6) を参照します。

[14.8.2.6 関数宣言からのテンプレート引数の推定] より:

1 declarator-id が関数テンプレートの特殊化を参照する宣言では、宣言が参照する特殊化を識別するために、テンプレート引数推定が実行されます。 具体的には、これは明示的なインスタンス化 (14.7.2)、明示的な特殊化 (14.7.3)、および特定のフレンド宣言 (14.5.4) に対して行われます。これは、解放関数テンプレートの特殊化が配置演算子 new (3.7.4.2、5.3.4) と一致するかどうかを判断するためにも行われます。これらすべての場合において、P は一致する可能性があると見なされる関数テンプレートの型であり、A は宣言からの関数型、または 5.3.4 で説明されている配置演算子 new に一致する解放関数の型のいずれかです。控除は、14.8.2.5 で説明されているように行われます。

2 そのように考慮された関数テンプレートのセットについて、部分的な順序付けが考慮された後 (14.5.6.2)、一致がないか、複数の一致がある場合、演繹は失敗し、宣言の場合、プログラムは不正な形式です。

あなたの場合、 declarator-id が特殊化を参照していないため、テンプレート引数の推論は実行されませwhose declarator-id refers to a specialization重要な部分は、それが起こるための条件だと思います。簡単に言えば<>、最初の文が14.8.2.6p1発生するために必要です(これを正しく読んでいる場合)。

UPDATE この状況での declarator-id とは何かを分析しましょう。

qualified-id:
nested-name-specifier templateopt unqualified-id
:: identifier
:: operator-function-id
:: literal-operator-id
:: template-id

上記の文法からわかるように、void ::operator*(A<T>, A<T>)は aであり、 では:: operator-function-idありません :: template-id。これは、構文がテンプレート関数を宣言できないことを意味します (エラー メッセージに記載されているように)。テンプレート ID にするためには、operator-function-id < template-argument-listopt>構文を使用する必要があります。

于 2012-11-24T00:16:04.737 に答える