【问题标题】:Why Does the Standard C++ Grammar Not Cover this Case?为什么标准 C++ 语法没有涵盖这种情况?
【发布时间】:2016-09-05 02:00:31
【问题描述】:

我主要指的是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&lt;int&gt; 语法引用全局命名空间中定义的模板。这与NamespaceA::CC&lt;int&gt; 语法不同,其中:: 运算符前面有一些东西。使用其他一些工具,我尝试严格使用 C++03 中的语法来解析它,但它给了我错误,在我看来,标准只接受类头声明中的 NamespaceA::CC&lt;int&gt; 形式。

仔细一看,问题在于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

【问题讨论】:

  • 当然嵌套名称说明符可以是::,而CC是标识符,...?
  • 我注意到 C++11 在嵌套名称说明符中添加了一个前导 '::' 而 c++03 标准要求在 ':: 之前有一个类或命名空间名称'。可能是 C++03 中的一个“错误”
  • @JavaMan link 在另一个问题中(搜索“355.”,# is nonfunctional)似乎完全表明了这一点。尚不清楚它是否已在 C++11 或 14 中修复,甚至还没有。

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


【解决方案1】:

来自 Columbo 的评论,

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

情况并非如此,至少在这个问题的上下文中不是。直到 2014 年版的 C++ 标准,裸双分号不符合作为 nested-name-specifier 的条件。该标准的 2003 版本表示 nested-name-specifier 采用 BNF 中的两种形式之一:

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

没有空间让裸class ::CC 适应此规范。 2011 版在 BNF 中为 nested-name-specifier 添加了很多内容:

  • ::opt类型名 ::
  • ::opt命名空间名称 ::
  • decltype-specifier ::
  • 嵌套名称说明符 标识符 ::
  • nested-name-specifier templateoptsimple-template-id ::

这仍然没有为class ::CC 留下空间。 2014 版标准最终解决了这个问题,说 nested-name-specifier 是其中之一

  • ::
  • 类型名称 ::
  • 命名空间名称 ::
  • decltype-specifier ::
  • 嵌套名称说明符 标识符 ::
  • nested-name-specifier templateoptsimple-template-id ::


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

另一种观点是,这不是错误。在 2003 和 2011 版本的标准中,nested-name-specifier 的 BNF 非常明确,因此 template&lt;&gt; class ::CC&lt;int&gt; {...} 不应编译。这是一个不幸的错误功能还是故意的功能并不重要。问题中的代码不应该从这个角度编译。

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

也就是说,既然标准已经明确,那么新版本的 GCC 中存在一个错误,因为即使指定了 --std=c++14,它们也不允许 template&lt;&gt; class ::CC&lt;int&gt; {...} 编译。

【讨论】:

    【解决方案2】:

    在 C++ 草案中,[class].11 中提到了 nested-name-specifier

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

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

    在您的代码中,您在模板类专业化中使用了class ::CC&lt;int&gt;[temp.expl.spec].2 也适用于此:

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

    因此,我认为,使用限定名称应该没问题。

    【讨论】:

    • 然而GCC 给出了一个错误,尽管Clang 可以优雅地处理它。看起来像一个 GCC 错误。
    • @rubenvb 是的,我还注意到 GCC 4.9 和 5.3.0 以及 Clang 3.7.1。
    • 此答案适用于 2014 版标准,但不适用于 2011 年及更早版本。 2011 年 3 月,第 355 期作为论文 N3259 被选入 WP。这是在论文 N3242 已被批准为 C++ 20011 标准草案之后。
    猜你喜欢
    • 2021-12-29
    • 2011-05-24
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 1970-01-01
    • 1970-01-01
    • 2019-10-15
    • 1970-01-01
    相关资源
    最近更新 更多