wil*_*llj 12 c++ templates language-lawyer
在测试是否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;
Run Code Online (Sandbox Code Playgroud)
通常,应编写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]
当成员模板特化的名称出现在postfix-expression 之后
.或之后,或者出现在->qualified-id中的嵌套名称说明符之后,以及postfix-expression的对象或指针表达式或者嵌套名称说明符中的qualified-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;
Run Code Online (Sandbox Code Playgroud)
Clang给出error: redefinition of 'g' with a different type,但g实际上匹配声明的类型.
我希望看到一个诊断建议使用typename或template.
这可以归结为第一个例子中clang的行为是无意的假设.
clang 和 gcc 是正确的。
\n\n编译器知道A<T1>::B<T2>引用的是一个类型并且B<T2>是一个模板,并且A<T1>::B<T2>::f是当前实例化的成员。因此,thetypename和template关键字不是必需的。
从 v14.6.2.1p4 开始:
\n\n\n\n\n名称是当前实例化的成员,如果它是
\n\n一个限定 ID,其中嵌套名称说明符引用当前实例化,并且在查找时至少引用当前实例化的一个\n 成员
\n
A<T1>::B<T2>是一个限定 ID,并且A<T1>::是引用当前实例化的嵌套名称说明符。我们知道 指的A<T1>::是 14.6.2.1p1 中的当前实例化:
\n\n\n名称指的是当前实例化,如果它是
\n\n\xe2\x80\x94 在主类模板或主类模板成员\n的定义中,类模板的名称,后跟\n 主模板的模板参数列表(如下所述)\n包含在 <> (或等效的模板别名专业化)中,
\n
在您的代码中,我们定义了主类模板的成员,即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>确实指的是如上所述的当前实例。