另一个是给语言律师的。
对于以下代码,无论是否存在显式实例化A<int>
:
clang 3.0 需要在andtypename
的声明中,但不是.Type1
Type3
Type2
gcc 4.8.1 要求typename
在声明中Type1
,但不是在Type2
orType3
中。
struct C
{
int f1();
static int f2();
int m;
};
template<int i>
struct S
{
typedef int Type;
};
template<typename T>
struct B : C
{
};
template<typename T>
struct A : B<T>
{
void f()
{
typedef typename S<sizeof(C::f1())>::Type Type1; // typename required
typedef S<sizeof(C::f2())>::Type Type2; // typename not required
typedef typename S<sizeof(C::m)>::Type Type3; // typename not required?
}
};
template struct A<int>;
我typename
认为在声明中是必需的,Type1
因为调用非静态成员的隐含对象参数是(*this)
,其类型是依赖的。参见[over.call.func]:
在非限定函数调用中,名称不由
->
or.
运算符限定,并且具有更通用的主表达式形式。按照函数调用中名称查找的正常规则,在函数调用的上下文中查找名称。通过该查找找到的函数声明构成候选函数集。由于名称查找的规则,候选函数集由 (1) 完全由非成员函数或 (2) 完全由某个类 T 的成员函数组成。在情况 (1) 中,参数列表与调用中的表达式列表。在情况 (2) 中,参数列表是调用中的表达式列表,通过添加隐含的对象参数来扩充,就像在限定的函数调用中一样。如果关键字this
在范围内并引用类T
,或 的派生类T
,则隐含的对象参数是(*this)
。
遵循这个逻辑,typename
在声明中不需要,Type2
因为成员是静态的。声明中也不typename
需要,Type3
因为表达式不是对非静态成员的调用。
我已经阅读了这篇文章:我必须在哪里以及为什么要放置“模板”和“类型名”关键字? .. 并通过 [temp.dep.type] 和 [temp.dep.expr] 中的规则。在确定表达式是否依赖时,我看不到任何指定是否应对非静态成员函数名称进行特殊处理的内容。标准是否规定了这一点?
编辑:删除了关于转换为基于 [class.mfct.non-static] 的类成员访问表达式的讨论 - 凯西的回答更详细地讨论了这一点。