C++ 是';typename';当模板参数包含非静态成员的名称时为必填项?
另一个是给语言律师的 对于以下代码,无论是否存在C++ 是';typename';当模板参数包含非静态成员的名称时为必填项?,c++,templates,language-lawyer,typename,C++,Templates,Language Lawyer,Typename,另一个是给语言律师的 对于以下代码,无论是否存在A的显式实例化: clang 3.0要求在声明Type1和Type3时使用typename,但不要求声明Type2 gcc 4.8.1要求在声明Type1时使用typename,但不使用Type2或Type3 struct C { int f1(); static int f2(); int m; }; template<int i> struct S { typedef int Type; }; t
A
的显式实例化:
clang 3.0要求在声明Type1
和Type3
时使用typename
,但不要求声明Type2
gcc 4.8.1要求在声明Type1
时使用typename
,但不使用Type2
或Type3
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>;
struct C
{
int f1();
静态int f2();
int m;
};
模板
结构
{
typedef int类型;
};
模板
结构B:C
{
};
模板
结构A:B
{
void f()
{
typedef typename S::Type Type1;//需要typename
typedef S::Type2;//不需要typename
typedef typename S::Type Type3;//是否不需要typename?
}
};
模板结构A;
I图Type1
的声明中需要typename
,因为调用非静态成员的隐含对象参数是(*this)
,其类型是依赖的。
请参见[over.call.func]:
在非限定函数调用中,名称不是由->
或
运算符限定的,它具有更一般的主表达式形式。按照函数调用中名称查找的常规规则,在函数调用的上下文中查找名称。通过该查找找到的函数声明构成候选函数集。由于名称查找的规则,候选函数集由(1)全部非成员函数或(2)部分T类的成员函数组成。在情况(1)中,参数列表与调用中的表达式列表相同。在案例(2)中,参数列表是调用中的表达式列表,通过添加隐含的对象参数(如在限定函数调用中)来增加。如果关键字this
在范围内,并且引用类T
,或派生类T
,则隐含的对象参数为
(*此)
按照此逻辑,Type2
的声明中不需要typename
,因为该成员是静态的。在Type3
的声明中也不需要typename
,因为表达式不是对非静态成员的调用
我通读了以下内容:
.. 并通过[temp.dep.type]和[temp.dep.expr]中的规则。我看不到任何东西指定在确定表达式是否依赖时,是否应该对非静态成员函数名进行特殊处理。标准是否规定了这一点
编辑:删除了关于基于[class.mfct.non-static]转换为类成员访问表达式的讨论——Casey的回答对此进行了更详细的讨论 我不完全确定,但这里有一个使用您的报价的解释:
只是一个普通函数,因此它是无条件已知的,并且C::f2
不依赖于模板参数sizeof(C::f2())
——它只是一个普通类型,就像T
s
实际上是C::f1()
,由于this->f1()
依赖于模板参数this
,T
是一个依赖类型,因此需要消除歧义Sf1())>
C
是通过非限定名称查找在阶段1查找期间解析的非依赖名称C::f1
和C::f2
也是在模板查找的第一阶段通过限定名称查找解析的非依赖名称。无论C
是否是a
的基础,这都是正确的
问题中引用的[class.mfct.non-static]文本似乎来自C++03。C++11文本(9.3.1/3)内容如下:
如果名称查找(3.4),则在类X
的成员中使用不属于类成员访问语法(5.2.5)一部分且不用于形成成员指针(5.3.1)的id表达式(5.1)时,可以使用此
(5.1.1)将id表达式中的名称解析为某个类的非静态非类型成员C
,如果id表达式是潜在计算的[emph.mine]或C
是X
或X的基类,则id表达式将转换为类成员访问表达式(5.2.5)使用(*this)
(9.3.2)作为操作符左侧的后缀表达式
C::f1
可能在可以使用this
的上下文中进行评估,因此C::f1
被转换为(*this).C::f1
。由于B
明确依赖于模板参数,(*this).C::f1
是14.6.2.1/5中未知专门化的成员。[本标准使用术语“未知专业化的成员”来表示可能表示模板某些专业化成员但并非所有专业化成员的表达式。]
由于(*this).C::f1
表示未知专门化的成员,因此根据14.6.2.2/5,它是一个类型相关的类成员访问表达式。根据14.6.2.2/1,扩展而言,(*this).C::f1()
依赖于类型<根据14.6.2.3/2,code>sizeof((*this).C::f1())
取决于值S
-一个带有值相关参数表达式的简单模板id-然后根据14.6.2.1/8成为相关类型
由于S
是依赖的
S::Type
根据14.6.2/1“如果id表达式的非限定id是一个模板id,其中任何模板参数都依赖于模板参数”。最后,但并非最不重要的是,14.6/2要求使用关键字typename
来指示依赖名指代类型:
typename S::Type
我想这就是全部的原因