typename
clang で が必要かどうかをテストしているときに、この奇妙な動作に遭遇しました。clang と gcc の両方がこのコードを受け入れますが、msvc は拒否します。
template<class T1>
struct A
{
template<class T2>
struct B
{
static B f;
static typename A<T2>::template B<T1> g;
};
};
template<class T1>
template<class T2>
typename A<T2>::template B<T1> // ok, typename/template required
A<T1>::B<T2>::g;
template<class T1>
template<class T2>
A<T1>::B<T2> // clang/gcc accept, msvc rejects missing typename
A<T1>::B<T2>::f;
一般に、qualified-id A<T1>::B<T2>
(A<T1>
は従属名) を記述する必要がありますtypename A<T1>::template B<T2>
。gcc/clang の動作は正しくありませんか、それともこの特定のケースで一般規則 (以下に引用) に例外がありますか?
A<T1>
は従属名ではない、またはB<T2>
現在のインスタンス化のメンバーを参照していると主張できます。ただし、型指定子を解析する時点では、現在のインスタンス化が であることを知ることはできませんA<T1>
。それA<T1>
が現在のインスタンス化であると推測することを実装に要求するのは問題があるようです。
14.6 名前解決 [temp.res]
テンプレートの宣言または定義で使用され、テンプレート パラメーターに依存する名前は、該当する名前検索で型名が検出されるか、名前がキーワード typename によって修飾されない限り、型に名前を付けないと見なされます。
14.2 テンプレートの特殊化の名前 [temp.names]
メンバ テンプレートの特殊化の名前が、修飾 ID の postfix-expression または nested-name-specifier の後または後に表示
.
さ->
れ、postfix-expression または nested-name-specifier のオブジェクトまたはポインタ式が修飾された ID は、テンプレート パラメーター (14.6.2) に依存しますが、現在のインスタンス化 (14.6.2.1) のメンバーを参照しません。メンバー テンプレート名の前にキーワード テンプレートを付ける必要があります。それ以外の場合、名前は非テンプレートの名前と見なされます。
ここでclangが何をしているのかをさらに調査するために、これも試しました:
template<class T1>
struct C
{
template<class T2>
struct D
{
static typename A<T1>::template B<T2> f;
static typename A<T1>::template B<T2> g;
};
};
template<class T1>
template<class T2>
typename A<T1>::template B<T2> // ok, typename/template required
C<T1>::D<T2>::f;
template<class T1>
template<class T2>
A<T1>::B<T2> // clang rejects with incorrect error
C<T1>::D<T2>::g;
Clang は を返しますerror: redefinition of 'g' with a different type
が、 の型はg
実際には宣言と一致します。
typename
代わりに、またはの使用を示唆する診断が表示されることを期待しますtemplate
。
これにより、最初の例での clang の動作は意図しないものであるという仮説が正当化されます。