为什么标准C ++语法不能涵盖这种情况?

时间:2016-05-10 08:06:28

标签: c++ c++11 language-lawyer grammar c++03

我主要是指C ++ 03标准,但在快速浏览之后,它也应该适用于C ++ 11标准。

以下代码在VC ++ 2010中成功编译和执行:

template<typename T> 
class CC { 
  public: 
    T f(T a) { 
            return a*a;
    } 
};
template<> 
class ::CC<int> {  //<--- ::CC<int> syntax allowed by VC++2010, but is it non-standard ?
  public: 
    int f(int a) { 
            return a*a;
    } 
};

int main(int argc, _TCHAR* argv[])
{
    ::CC<int> c;
}

请注意::CC<int>语法以引用全局命名空间中定义的模板。这与NamespaceA::CC<int>运算符前面的::运算符不同。使用其他一些工具,我试图使用C ++ 03中的语法严格解析它,但它给了我错误,在我看来标准只接受类头声明中的NamespaceA::CC<int>形式。

仔细看看,问题是class-head是由标准中的语法定义的:

class-head:
   class-key identifier(optional) base-clause(optional)
   class-key nested-name-specifier identifier base-clause(optional)
   class-key nested-name-specifier(optional) template-id base-clause(optional)

由于nested-name-specifier的格式为AA::bb:: ...,因此它不接受我的::CC。 我的问题是,为什么C ++标准不允许:: CC形式?这只是我对标准语法的错误解释吗?正确的语法应该是这样的:

class-head:
   ...
   class-key '::'(optional) nested-name-specifier(optional) template-id base-clause(optional)

注意,上面的表单实际上是由其他地方的标准使用,例如,在指定declarator-id时使用:

declarator-id:
   id-expression
   ::(optional) nested-name-specifier(optional) class-name

2 个答案:

答案 0 :(得分:3)

来自Columbo的评论,

  

当然,嵌套名称说明符可以是::,而CC是标识符,......?

事实并非如此,至少不是在这个问题的背景下。直到2014版C ++标准,一个裸双分号不符合嵌套名称说明符。该标准的2003版本表示嵌套名称说明符采用BNF中的两种形式之一:

  • class-or-namespace-name :: nested-name-specifier opt
  • class-or-namespace-name :: template nested-name-specifier

没有空间让class ::CC符合此规范。 2011版为BNF添加了相当多的嵌套名称说明符

  • :: opt type-name ::
  • :: opt namespace-name ::
  • decltype-specifier ::
  • 嵌套名称说明符 标识符 ::
  • 嵌套名称说明符 template opt simple-template-id ::

这仍然没有为class ::CC留下空间。该标准的2014版最终通过说嵌套名称说明符

之一来解决这个问题。
  • ::
  • type-name ::
  • namespace-name ::
  • decltype-specifier ::
  • 嵌套名称说明符 标识符 ::
  • 嵌套名称说明符 template opt simple-template-id ::


有很多方法可以看到这个有趣的功能&#34;。一个是这是语言规范中长期存在的错误,最初在2002年被确定为issue #355。编译器供应商的工作之一是识别和修补语言规范中的错误,然后在即将发布的标准版本中修复这些错误。从这个角度来看,template<> class ::CC<int> {...}应该编译。

另一种观点是,是一个错误。 2003和2011版标准中的嵌套名称说明符的BNF非常清楚,因此template<> class ::CC<int> {...}不应编译。这不是一个不幸的错误或故意的特征并不重要。问题中的代码不应该从这个角度进行编译。

哪种观点是正确的是值得商榷的。首先报告这种差异的问题没有被拒绝,这表明该报告有一些内容。另一方面,通过标准的两次修订,没有做任何事情也说了些什么。

也就是说,既然标准已经澄清,GCC的新版本就会出现一个错误,因为即使有人指定--std=c++14,它们也不允许编译template<> class ::CC<int> {...}

答案 1 :(得分:2)

在C ++草案中,[class].11中提到了嵌套名称说明符

  

如果类头名包含嵌套名称说明符,则类说明符应引用先前在嵌套名称说明符引用的类或命名空间中直接声明的类,或者在该命名空间的内联命名空间集([namespace.def])的元素(即,不仅仅是由using-declaration继承或引入),并且类说明符应出现在包含前一个声明的命名空间中。在这种情况下,定义的类头名的嵌套名称说明符不应以decltype-specifier开头。

根据[expr.prim.id.qual],它当然也可以是Array

在您的代码中,您在模板类专门化中使用IList<int>[temp.expl.spec].2也适用于此类:

  

应在包含专用模板的命名空间中声明显式特化。其声明者id或类头名未被限定的显式特化应在模板的最近的封闭命名空间中声明,或者,如果命名空间是内联的([namespace.def]),则从其封闭的命名空间集合中声明任何命名空间。这样的声明也可以是一个定义。如果声明不是定义,则可以稍后定义专门化([namespace.memdef])。

因此,我认为,使用合格的名称应该没问题。