在测试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的类型实际上与声明匹配。

相反,我希望看到诊断建议使用typenametemplate

这归因于以下假设:在第一个示例中,c的行为是意料之外的。

最佳答案

clang和gcc是正确的。

编译器知道A<T1>::B<T2>引用类型,并且B<T2>是模板,并且A<T1>::B<T2>::f是当前实例的成员。因此,不需要typenametemplate关键字。

从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/

10-11 23:05
查看更多