wil*_*llj 7 c++ templates language-lawyer compiler-bug dependent-type
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;
};
Run Code Online (Sandbox Code Playgroud)
This is covered by the following wording in the C++11 standard:
[except]/2
A throw-expression is of type void.
[expr.delete]/1
The operand shall have a pointer to object type, or a class type having a single non-explicit conversion function to a pointer to object type. The result has type void.
So I'm assuming decltype
produces void
in both cases.
[expr.const]/2
A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression
a new-expression
a throw-expression
This suggests that an expression involving either throw
or delete
cannot be a constant expression.
[temp.dep.type]/8
A type is dependent if it is
a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent
denoted by
decltype(expression)
, where expression is type-dependent
So B<decltype(..)>
is dependent only if the expression is type-dependent.
[temp.dep.expr]/4
Expressions of the following forms are never type-dependent (because the type of the expression cannot be dependent):
Run Code Online (Sandbox Code Playgroud)delete cast-expression throw assignment-expression
This suggests that neither expression can be type-dependent.
Are gcc and clang both wrong?
让我们回到需要的时候typename
.§14.6[temp.res]/p3,所有报价均来自N4140:
当qualified-id旨在引用不是当前实例化成员的类型(14.6.2.1)并且其 嵌套名称说明符引用依赖类型时,它应以关键字为前缀
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,其中模板名称是模板参数或任何模板参数是依赖类型或依赖于类型或依赖于值的表达式,或者
(8.8) - 由
decltype(
表达式表示)
,其中表达式依赖于类型(14.6.2.2).
B<decltype(throw (int*)n)>
是一个简单的模板ID.模板名称B
不是模板参数.唯一的模板参数decltype(throw (int*)n)
不是表达式,因此B<decltype(throw (int*)n)>
仅在decltype(throw (int*)n)
依赖类型时才依赖.decltype(throw (int*)n)
反过来,每子弹8.8,只依赖于throw (int*)n
类型依赖.但我们知道,根据§14.6.2.2[temp.dep.expr]/p4:
以下表单的表达式从不依赖于类型(因为表达式的类型不能依赖):
[...]
::
optdelete
cast-expression[...]
throw
赋值表达式选择[...]
因此,throw (int*)n
它不依赖decltype(throw (int*)n)
于类型,因此不是依赖类型,因此B<decltype(throw (int*)n)>
不是依赖类型,因此typename
不需要B<decltype(throw (int*)n)>::Type
,因此,这是编译器错误.