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;
通常,应编写一个限定 ID A<T1>::B<T2>
(其中是从属名称) 。gcc/clang 的行为是否不正确,或者在这种特殊情况下,一般规则(引用如下)是否存在例外?A<T1>
typename A<T1>::template B<T2>
可以说这A<T1>
不是从属名称,或者是B<T2>
指当前实例化的成员。但是,在解析类型说明符时,不可能知道当前实例化是A<T1>
. 要求实现猜测A<T1>
是当前实例化似乎是有问题的。
14.6 名称解析 [temp.res]
在模板声明或定义中使用并且依赖于模板参数的名称被假定为不命名类型,除非适用的名称查找找到类型名称或该名称由关键字 typename 限定。
14.2 模板特化的名称 [temp.names]
当成员模板特化的名称出现在后缀表达式之后
.
或->
之后,或者在限定 ID 中的嵌套名称说明符之后,并且后缀表达式或嵌套名称说明符中的对象或指针表达式出现在qualified-id 依赖于模板参数 (14.6.2) 但不引用当前实例化的成员 (14.6.2.1),成员模板名称必须以关键字 template 为前缀。否则,该名称被假定为命名非模板。
为了进一步调查 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
or的诊断template
。
这证明了第一个示例中 clang 的行为是无意的假设。