在测试clang是否需要typename
时,我遇到了这种奇怪的行为。 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>
(其中A<T1>
是从属名称)写为typename A<T1>::template B<T2>
。 gcc/clang的行为是否不正确,或者在这种特殊情况下,通用规则(在下面引用)是否有异常(exception)?可以说
A<T1>
不是从属名称,或者B<T2>
引用当前实例的成员。但是,在解析类型说明符时,不可能知道当前实例是A<T1>
。要求实现猜测A<T1>
是当前实例似乎有问题。为了进一步研究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
。这归因于以下假设:在第一个示例中,c的行为是意料之外的。
最佳答案
clang和gcc是正确的。
编译器知道A<T1>::B<T2>
引用类型,并且B<T2>
是模板,并且A<T1>::B<T2>::f
是当前实例的成员。因此,不需要typename
和template
关键字。
从v14.6.2.1p4:
A<T1>::B<T2>
是合格ID,而A<T1>::
是嵌套名称说明符,它引用当前实例。我们知道A<T1>::
引用了14.6.2.1p1中的当前实例:
在您的代码中,我们定义了主类模板的成员,即A<T1>::B<T2>::f
,并且A<T1>
是类模板的名称,后跟主模板的模板参数列表。
在您的问题中,您说However, at the point of parsing the type-specifier it's not possible to know that the current instantiation is A<T1>
。但是,我无法理解,因为名称A<T1>
确实引用了如上所述的当前实例。
关于c++ - 超出成员定义的类型说明符中可以省略typename吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18344580/