7

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 表达式的类型为 void。

[expr.delete]/1

操作数应具有指向对象类型的指针,或具有指向对象类型指针的单个非显式转换函数的类类型。结果类型为 void。

所以我假设在这两种情况下都会decltype产生void

[expr.const]/2

条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式

  • 新表达

  • 抛出表达式

这表明包含throwdelete不能是常量表达式的表达式。

[温度.dep.type]/8

一个类型是依赖的,如果它是

  • 一个 simple-template-id,其中模板名称是模板参数或任何模板参数是依赖类型或依赖于类型或值的表达式

  • 由 表示decltype(expression),其中表达式依赖于类型

仅当表达式依赖于类型时, SoB<decltype(..)>才依赖。

[温度.dep.expr]/4

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

delete cast-expression
throw assignment-expression

这表明两个表达式都不能依赖于类型。

gcc 和 clang 都错了吗?

4

1 回答 1

7

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

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

在这种情况下, qualified-idB<decltype(throw (int*)n)>::Type(和delete分析完全相同的版本)。typename如果nested-name-specifierB<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.8throw (int*)n条,仅在依赖于类型时才依赖。但我们知道,根据 §14.6.2.2 [temp.dep.expr]/p4:

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

[...]

::选择 delete 强制转换表达式

[...]

throw 赋值表达式选择

[...]

因此,throw (int*)ndecltype(throw (int*)n)依赖于类型,因此不是依赖类型,因此B<decltype(throw (int*)n)>也不是依赖类型,因此typename不需要B<decltype(throw (int*)n)>::Type,所以是的,这是一个编译器错误。

于 2014-12-30T23:33:28.360 回答