【发布时间】:2015-02-27 14:47:14
【问题描述】:
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
一个类型是依赖的,如果它是
- 一个简单模板ID,其中模板名称是模板参数或任何模板 arguments 是依赖类型或依赖于类型或值的表达式
如果sizeof m 是值相关的,那么I<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
在 非静态成员函数的定义,命名非静态成员的标识符被转换为 类成员访问表达式
所以m 是类成员访问表达式中的成员。
[temp.dep.type]/4
一个名字是一个当前实例化的成员如果它是
- 一个 id 表达式,表示类成员访问表达式 (5.2.5) 中的成员,其类型 对象表达式的当前实例化和 id 表达式,当查找时(3.4.5), 指当前实例化或其非依赖基类的至少一个成员。
所以看来m 是当前实例化的成员。
[temp.dep.type]/5
如果名字是未知专业化的成员,则它是
一个 id 表达式,表示类成员访问表达式 (5.2.5) 中的成员,其中任一
对象表达式的类型是当前实例化,当前实例化至少有 一个依赖基类,并且 id-expression 的名称查找未找到 当前实例化或其非依赖基类;或
对象表达式的类型是依赖的,不是当前的实例化。
所以m 不是未知特化的成员 - 通过名称查找会发现它是当前实例化的成员。
[temp.dep.expr]/3
如果一个 id 表达式包含,则它是类型相关的
- 通过名称查找关联的标识符与一个或多个声明为依赖类型的声明,
- 嵌套名称说明符或限定 ID,用于命名未知专业化的成员
由于m 属于int 类型并且不是未知专业化的成员,因此这些项目符号都不会使id 表达式m 依赖。
[temp.dep.expr]/5
类成员访问表达式 (5.2.5) 如果表达式引用当前的成员,则依赖于类型。 实例化和被引用成员的类型依赖,或者类成员访问表达式 指代未知专业的成员。
当m 转换为类成员访问表达式时,它仍然不依赖,因为它不引用未知特化的成员。
应该将m 视为依赖项吗?在相关的说明中,this->m 是否应该被视为依赖? std::declval<A>().m 呢?
编辑
最后,&A::m 应该依赖吗?
【问题讨论】:
-
我怎么知道你会问这个问题? :) 我认为从您之前的问题中可以清楚地看出标准措辞不当,或者编译器没有正确实施标准。您可能会找到更多示例,其中根据标准应该不依赖的类型或表达式被编译器视为依赖。
-
您的问题似乎是:“我已经得出结论,
sizeof m不依赖,但编译器似乎认为这是依赖。我的证明有缺陷吗?”答案是:“不,我看不到任何缺陷。”相同的参数应该适用于this->m和std::declval<A>().m,因为在任何情况下对象表达式都引用当前实例化,而m没有依赖类型。 -
我认为这些问题的答案将来可能对其他人有用 - 诚然只对那些正在实施 C++ 的人。
-
std::declval<A>().m很复杂,因为类成员访问的左侧是类型相关的,因此无法(?)确定它是否引用当前实例化 -
m翻译成的类成员访问表达式不就是this->m吗?所以如果这是有问题的,那么简单的m。
标签: c++ templates c++11 language-lawyer dependent-type