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

时间:2014-12-30 22:46:03

标签: c++ templates language-lawyer dependent-type compiler-bug

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

  •   

这表明涉及throwdelete的表达式不能是常量表达式。

  

[temp.dep.type] / 8

     

如果类型是

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

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

  •   

所以B<decltype(..)>仅在表达式依赖于类型时才依赖。

  

[temp.dep.expr / 4

     

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

delete cast-expression
throw assignment-expression

这表明两种表达都不依赖于类型。

gcc和clang都错了吗?

1 个答案:

答案 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:

  

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

     

[...]

     

:: opt delete cast-expression

     

[...]

     

throw assignment-expression opt

     

[...]

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