gcc 5.0和clang 3.6都需要以下示例中的typename
关键字:
template<typename T>
struct B
{
typedef int Type;
};
template<int n>
struct A
{
typedef typename B<decltype(throw (int*)n)>::Type Throw;
typedef typename B<decltype(delete (int*)n)>::Type Delete;
};
C ++ 11标准中的以下措辞涵盖了这一点:
[除] / 2
throw-expression的类型为void。
[expr.delete] / 1
操作数应具有指向对象类型的指针,或具有单个非显式转换的类类型 函数指向对象类型的指针。结果类型为void。
所以我假设decltype
在两种情况下都会产生void
。
[expr.const] / 2
条件表达式是核心常量表达式,除非它可能涉及以下之一 评估子表达式
一个新表达式
一个throw-expression
这表明涉及throw
或delete
的表达式不能是常量表达式。
[temp.dep.type] / 8
如果类型是
,则类型是依赖的
一个simple-template-id,其中模板名称是模板参数或任何模板 arguments是依赖类型或依赖于类型或依赖于值的表达式
由
decltype(expression)
表示,其中表达式依赖于类型
所以B<decltype(..)>
仅在表达式依赖于类型时才依赖。
[temp.dep.expr / 4
以下表单的表达式从不依赖于类型(因为表达式的类型不能 依赖性):
delete cast-expression throw assignment-expression
这表明两种表达都不依赖于类型。
gcc和clang都错了吗?
答案 0 :(得分:7)
让我们回到需要typename
的时候。 §14.6[temp.res] / p3,所有报价均来自N4140:
当 qualified-id 旨在引用不是a的类型时 当前实例化的成员(14.6.2.1)及其成员 nested-name-specifier 是指依赖类型,它应以关键字
typename
作为前缀,形成 typename-specifier 。
在这种情况下, qualified-id 是B<decltype(throw (int*)n)>::Type
(和delete
版本,分析完全相同)。因此,如果嵌套名称说明符或typename
引用依赖类型,则需要B<decltype(throw (int*)n)>::
。
§14.6.2.1[temp.dep.type] / p8说,省略了六个不相关的子弹,
如果类型是
,则类型是依赖的[...]
(8.7) - 一个 simple-template-id ,其中模板名称是a 模板参数或任何模板参数都是依赖的 类型或类型相关或依赖于值的表达式,或
(8.8) - 由
decltype(
表达式)
表示,其中表达式与类型有关(14.6.2.2)。
B<decltype(throw (int*)n)>
是 simple-template-id 。模板名称B
不是模板参数。唯一的模板参数decltype(throw (int*)n)
不是表达式,因此B<decltype(throw (int*)n)>
仅在decltype(throw (int*)n)
是依赖类型时才依赖。反过来,每个子弹8.8 decltype(throw (int*)n)
仅在throw (int*)n
依赖于类型时才依赖。但我们知道,根据§14.6.2.2[temp.dep.expr] / p4:
以下形式的表达从不依赖于类型(因为 表达式的类型不能依赖):
[...]
::
optdelete
cast-expression[...]
throw
assignment-expression opt[...]
因此,throw (int*)n
不依赖于类型,因此decltype(throw (int*)n)
不是依赖类型,因此B<decltype(throw (int*)n)>
不是依赖类型,因此typename
是B<decltype(throw (int*)n)>::Type
不需要,所以是的,这是一个编译器错误。