gcc 5.0和clang 3.6都需要以下示例中的typename
关键字:
template<int n>
struct I
{
typedef int Type;
};
template<typename T>
struct A
{
int m;
void f()
{
typedef typename I<sizeof m>::Type Type; // typename required
}
};
C ++ 11标准中的以下措辞涵盖了这一点:
[temp.dep.type] / 8
如果类型是
,则类型是依赖的
- 一个simple-template-id,其中模板名称是模板参数或任何模板 arguments是依赖类型或依赖于类型或依赖于值的表达式
因此I<sizeof m>
取决于sizeof m
是否依赖于值。
[temp.dep.expr / 4
以下表单的表达式从不依赖于类型(因为表达式的类型不能 依赖性):
sizeof unary-expression
[temp.dep.constexpr] / 2
如果一元表达式或表达式是类型依赖的,则以下形式的表达式是值依赖的 或者type-id是依赖的:
sizeof unary-expression
所以sizeof m
仅在m
依赖时才依赖。
[expr.prim.general] / 8
内 定义非静态成员函数,将非静态成员命名的标识符转换为a 类成员访问表达式
因此m
是类成员访问表达式中的成员。
[temp.dep.type / 4
名称是当前实例化的成员,如果它是
- 一个id-expression,表示类成员访问表达式(5.2.5)中的成员 对象表达式是当前实例化,而id-expression在查找时(3.4.5), “当前实例化”是指当前实例化的至少一个成员或其非依赖性基类。
所以似乎m
是当前实例化的成员。
[temp.dep.type] / 5
如果名称是
,则该名称是未知专业化的成员
一个id-expression,表示类成员访问表达式(5.2.5)中的成员
对象表达式的类型是当前实例化,当前实例化至少有 一个依赖的基类,并且id-expression的名称查找找不到该成员 当前实例化或其非依赖基类;或
对象表达式的类型是依赖的,不是当前的实例化。
所以m
不是未知专业化的成员 - 通过名称查找可以找到它是当前实例化的成员。
[temp.dep.expr] / 3
如果id-expression包含
,则它依赖于类型
- 通过名称查找与一个或多个使用依赖类型声明的声明相关联的标识符
- 用于命名未知专业化成员的嵌套名称说明符或qualified-id
由于m
属于int
类型,并且不是未知专业化的成员,因此这些项目符号都不会使id-expression m
依赖。
[temp.dep.expr] / 5
如果表达式引用当前成员,则类成员访问表达式(5.2.5)依赖于类型 实例化和引用的成员的类型是依赖的,或类成员访问表达式 是指未知专业化的成员。
当m
转换为类成员访问表达式时,它仍然不依赖,因为它不引用未知专门化的成员。
m
应该被视为依赖吗?在相关的说明中,this->m
应该被视为依赖吗?那么std::declval<A>().m
呢?
修改
最后,&A::m
应该依赖吗?
答案 0 :(得分:2)
正如您所说,sizeof m
已转换为sizeof (*this).m
sizeof
仅在参数表达式依赖于类型时依赖,而不是,根据[temp.dep.expr] / 5:
如果是,类成员访问表达式(5.2.5)依赖于类型 表达式是指当前实例化的成员和 引用成员的类型是依赖的,还是类成员访问 表达是指未知专业化的成员。
m
的类型不依赖,表达式也不引用未知专业化的成员 - [temp.dep.type] / 6:
如果名称是
,则该名称是未知专业化的成员
- id-expression 表示类成员访问表达式(5.2.5)中的成员,其中
- 对象表达式的类型是当前实例化,当前实例化至少有一个从属基类,并且 id-expression 的名称查找未找到类的成员 这是当前实例化或非依赖基类 物;或
- 对象表达式的类型是依赖的,而不是当前的实例化。
即使(*this)
的类型是依赖的,它也是当前的实例化。名称查找应该发现m
是当前实例化的成员。
因此*this
不依赖于类型,因此sizeof (*this).m
不依赖。 (sizeof m
也不依赖于函数定义的任何非静态数据成员初始值设定项,我在第二个删除的答案中意外地介绍了这一点。
要sizeof std::declval<A>().m
依赖,std::declval<A>().m
必须依赖于类型
std::declval<A>().m
似乎依赖于类型,但我不确定。正如我在上面引用的[temp.dep.expr] / 5中所指定的那样,唯一的可能性是表达式中的m
是未知特化的成员,我们必须证明它是。
如果名称是
,则该名称是未知专业化的成员
- id-expression 表示类成员访问表达式(5.2.5)中的成员,其中
- 对象表达式的类型是当前实例化,当前实例化至少有一个从属基类,并且 id-expression的名称查找找不到类的成员 这是当前实例化或非依赖基类 物;或
- 对象表达式的类型取决于当前实例化。
以下是事实:
对象表达式std::declval<A>()
与类型有关。
std::declval<A>
的查找仅在定义上下文中完成,因为它是 qualified-id ,它们永远不是依赖名称([ temp.dep] / 1)。
通过限定名称查找只找到一个declval
函数模板,但是我们无法知道该候选的返回类型是否是定义时的当前实例化。特别地,add_rvalue_reference
可能具有在定义时未知的特化(类似于this one的场景)。因为我们不知道std::declval<A>()
是否是当前的实例化,(我们假设)它不是,这使得整个表达式依赖于类型。
&A::m
的格式为&
qualified-id ,[temp.dep.constexpr] / 5涵盖:
表单
&
qualified-id ,其中 qualified-id 命名当前实例化的依赖成员是 值相关。
[temp.dep.type] / 5:
名称是当前实例化的依赖成员(如果它是a) 当前实例化的成员,当查找时,指的是 至少有一个类是当前实例化的成员。
显然A::m
是当前实例化的成员,因此&A::m
与值有关。
此外,&A::m
依赖于类型:根据[temp.local],子表达式A
相当于A<T>
,这是一个 simple-template-id 依赖模板参数。
答案 1 :(得分:1)
答案取决于是否可以查找m
以确定它是当前实例化的成员。 id-expression m
转换为类成员访问(*this).m
,这意味着类成员访问中的限定名称查找规则适用。
通常,无法确定类型相关表达式的类型。尚不完全清楚是否应对(*this).
和this->
进行例外处理。包含this
的表达式取决于类型,但(*this).
和this->
都明确命名当前实例。
m
表达式m
确实不依赖于类型,因为它引用了当前实例化的成员。
在非静态成员的上下文中,m
被转换为类成员访问表达式(*this).m
。
[class.mfct.non静电] / 3
当一个id-expression(5.1)不属于类成员访问语法(5.2.5)并且不用于形成指向成员(5.3.1)的指针时,在类{{1>的成员中使用在可以使用它的上下文中(5.1.1),如果名称查找(3.4)将id-expression中的名称解析为某个类
X
的非静态非类型成员,并且要么id-expression可能被评估,要么C
是C
或者基类X
,id-expression将被转换为类成员访问表达式(5.2.5){ {1}}
发生转换是因为X
是类(*this)
的非静态成员中使用的类m
的成员。
[expr.prim.general] / 3
如果声明声明了一个类
A
this`的成员函数或成员函数模板 是可选的cv-qualifer-seq和结尾之间的类型“指向cv-qualifier-seq X的指针”的prvalue function-definition,member-declarator或declarator。它不会出现在可选的cv-qualifier-seq之前 它不应出现在静态成员函数的声明中(尽管它的类型和值 category在静态成员函数中定义,因为它们在非静态成员函数中。)[expr.prim.general] / 5
表达式
A
不得出现在任何其他上下文中。 [例如:
X, the expression
-end example]
上面的示例明确允许在非静态成员的this
表达式中使用class Outer {
int a[sizeof(*this)]; // error: not inside a member function
unsigned int sz = sizeof(*this); // OK: in brace-or-equal-initializer
void f() {
int b[sizeof(*this)]; // OK
struct Inner {
int c[sizeof(*this)]; // error: not inside a member function of Inner
};
}
};
。
[temp.dep.expr] / 2
如果封闭成员函数的类类型依赖,则
this
依赖于类型
因此sizeof
在类模板的成员函数的定义中依赖于类型。
[temp.dep.expr] / 1
除非如下所述,否则如果任何子表达式依赖于类型,则表达式依赖于类型。
但是,问题中引用的[temp.dep.expr] / 5中的例外推翻了上述内容。
this
表达式this
也不依赖于类型,因为它也是一个类成员访问表达式,它引用当前实例化的成员。
this->m
表达式this->m
必须依赖于类型,因为std::declval<A>().m
的返回类型可能取决于std::declval<A>().m
的类型。
[temp.local] / 1
与普通(非模板)类一样,类模板具有注入类名(第9节)。注入的类 - name可以用作模板名称或类型名称。当它与template-argument-list一起使用时, 作为模板模板参数的模板参数,或者作为详细说明类型的最终标识符 朋友类模板声明,它指的是类模板本身。否则,它是等价的 到模板名称后面跟着
中包含的类模板的模板参数std::declval<A>()
因此,A
会转换为<>
。
[temp.dep.type] / 8
如果类型是
,则类型是依赖的
模板参数
一个simple-template-id,其中模板名称是模板参数或任何模板 arguments是依赖类型或依赖于类型或依赖于值的表达式
这确认A
是一种依赖类型,这意味着A<T>
也是依赖类型。
[temp.dep.expr] / 3
如果id-expression包含
,则它依赖于类型
- 依赖的模板ID,
所以A<T>
是一个依赖于类型的表达式。因此,A
依赖于类型,因为它包含依赖于类型的子表达式std::declval<A>
。
std::declval<A>().m
表达式std::declval<A>
逻辑上必须依赖于类型,因为它具有类型&A::m
,这是一种依赖类型。
表达式&A::m
转换为int A<T>::*
,因为&A::m
是注入的类名 - 如上所示。
根据[temp.dep.expr] / 3,id-expression &A<T>::m
依赖于类型,因为它包含依赖的模板ID A
。因此,根据[temp.dep.expr] / 1,表达式A<T>::m
依赖于类型。
A<T>
表达式&A<T>::m
转换为A::m
,因为A::m
是注入的类名 - 如上所示。表达式A<T>::m
进一步转换为A
,因为A<T>::m
命名(*this).A<T>::m
的非静态成员。
根据[temp.dep.expr] / 3,id-expression A<T>::m
依赖于类型,因为它包含依赖的模板ID A
。类成员访问表达式A<T>::m
引用当前实例化的成员,因此[temp.dep.expr] / 5适用但不会使表达式依赖于类型 - 它也不会与[temp.dep.expr]相矛盾] / 3。
鉴于上述解释,命名A<T>
成员{id} (*this).A<T>::m
或A
的id-expression将变为类型依赖,这似乎是不必要和不一致的。请注意,使用A
或A<T>
限定的类型名称不会依赖于相同的上下文。
如果[temp.dep.expr] / 5的意图是A
与类型无关,那么A<T>
也应该不依赖于类型。另一方面,意图可能是非静态成员的名称始终是依赖的。我已向std-discussion发布了一个问题,以澄清这一点:https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/gEvZ7mmXEC8