在以下示例中,clang 3.6和gcc 5.0都需要typename
:
template<typename T>
struct B
{
typedef int Type;
};
void f(int);
template<int n>
struct A
{
typedef typename B<decltype(f(n))>::Type Type;
};
C ++ 11标准中的以下措辞涵盖了这一点:
[temp.dep.type] / 5
名称是未知专业化的成员,如果它是
- 一个qualified-id,其中嵌套名称说明符命名一个非当前的依赖类型 实例
[temp.dep.type] / 8
如果类型是
,则类型是依赖的
未知专业化的成员,
一个simple-template-id,其中模板名称是模板参数或任何模板 arguments是依赖类型或依赖于类型或依赖于值的表达式
由
decltype(expression)
表示,其中表达式依赖于类型
这表明B<decltype(f(n))>::Type
仅在B<decltype(f(n))>
与类型相关时才依赖于类型。此外,B<decltype(f(n))>
仅在f(n)
与类型相关时才依赖。
[temp.dep.expr] / 1
除非如下所述,否则如果任何子表达式依赖于类型,则表达式依赖于类型。
[temp.dep.expr] / 3
如果id-expression包含
,则它依赖于类型
通过名称查找关联的标识符,其中一个或多个声明使用依赖类型声明,
依赖的模板ID,
指定依赖类型的conversion-function-id,或
用于命名未知专业化成员的嵌套名称说明符或qualified-id;
或者如果它命名当前实例化的静态数据成员,该实例的类型为“未知绑定的数组” T“对于某些T
这表明,只有f(n)
依赖于类型且n
不类型相关时,n
才是类型相关的。
我错过了什么,或者这是编译错误?
答案 0 :(得分:2)
您的分析有点不完整,但正确无误。
当 qualified-id 旨在引用不是a的类型时 当前实例化的成员(14.6.2.1)及其成员 nested-name-specifier 是指依赖类型,它应以关键字
typename
作为前缀,形成 typename-specifier 。
显然B<…>::Type
不能引用当前实例化的成员。所以问题是B<decltype(f(n))>
是否是依赖类型。
如果类型是
,则类型是依赖的
- [...]
- 未知专业的成员,
- [...]
- 一个 simple-template-id ,其中模板名称是模板参数或任何模板参数是依赖项 类型或类型相关或依赖于值的表达式,或
- 由
decltype(expression)
表示,其中表达式与类型有关(14.6.2.2)。
f(n)
不依赖于类型,因为没有子表达式,因此decltype(f(n))
不是依赖类型。
此外,decltype(...)
不能依赖于值,因为它不是常量表达式 1 ,并且[temp.dep.constexpr]中没有其他段落适用。显然,decltype(f(n))
也不依赖于类型
只有当模板参数是依赖类型 2 时,B<decltype(f(n))>::Type
才会成为未知专业化的成员,我们认为它不是
因此,根据我的解释,编译器不正确,并且不需要typename
关键字。
1 事实上,decltype(…)
根本不是表达。
2 [temp.dep.type] / 5:
如果名称是
,则该名称是未知专业化的成员
- qualified-id ,其中 nested-name-specifier 命名的依赖类型不是当前的实例化。
- qualified-id ,其中嵌套名称说明符引用当前实例化,当前实例化至少有一个 依赖基类, qualified-id 的名称查找不会 找到当前实例化的类的任何成员或者 非依赖基类。
- id-expression 表示类成员访问表达式(5.2.5)中的成员,其中任何一个
- [...]