抛出或删除表达式是否可以依赖?

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):

delete cast-expression
throw assignment-expression
Run Code Online (Sandbox Code Playgroud)

This suggests that neither expression can be type-dependent.

Are gcc and clang both wrong?

T.C*_*.C. 7

让我们回到需要的时候typename.§14.6[temp.res]/p3,所有报价均来自N4140:

qualified-id旨在引用不是当前实例化成员的类型(14.6.2.1)并且其 嵌套名称说明符引用依赖类型时,它应以关键字为前缀typename,形成typename -specifier.

在这种情况下,qualified-idB<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:

以下表单的表达式从不依赖于类型(因为表达式的类型不能依赖):

[...]

::opt delete cast-expression

[...]

throw 赋值表达式选择

[...]

因此,throw (int*)n它不依赖decltype(throw (int*)n)于类型,因此不是依赖类型,因此B<decltype(throw (int*)n)>不是依赖类型,因此typename不需要B<decltype(throw (int*)n)>::Type,因此,这是编译器错误.