【发布时间】:2013-08-23 01:36:06
【问题描述】:
在测试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 的行为是否不正确,或者在这种特殊情况下,一般规则(下面引用)是否存在例外?
可以说A<T1> 不是从属名称,或者B<T2> 指的是当前实例化的成员。但是,在解析类型说明符时,不可能知道当前实例化是A<T1>。要求实现猜测A<T1> 是当前实例化似乎是有问题的。
14.6 名称解析 [temp.res]
在模板声明或定义中使用并且依赖于模板参数的名称是 假定不命名类型,除非适用的名称查找找到类型名称或名称是合格的 通过关键字类型名。
14.2 模板特化的名称 [temp.names]
当成员模板特化的名称出现在后缀表达式中的
.或->之后或之后 限定 ID 中的嵌套名称说明符,以及后缀表达式的对象或指针表达式或 限定 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 的行为是无意的假设。
【问题讨论】:
-
我个人会添加
typename... 但现在还没有动力深入研究标准 :) -
@dribeas 不用担心;)。您现在一定已经厌倦了所有这些语言律师问题!
-
无论是对还是错,只要找到 gcc 和 clang 接受的 any 代码,您都应该获得支持,但 VC++ 由于缺少
typename而拒绝.
标签: c++ templates language-lawyer