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

Why Does the Standard C++ Grammar Not Cover this Case?

我这里主要是指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 的语法来解析它,但它给了我错误,在我看来标准只接受 class 头声明中的 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

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

中提到

If a class-head-name contains a nested-name-specifier, the class-specifier shall refer to a class that was previously declared directly in the class or namespace to which the nested-name-specifier refers, or in an element of the inline namespace set ([namespace.def]) of that namespace (i.e., not merely inherited or introduced by a using-declaration), and the class-specifier shall appear in a namespace enclosing the previous declaration. In such cases, the nested-name-specifier of the class-head-name of the definition shall not begin with a decltype-specifier.

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

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

An explicit specialization shall be declared in a namespace enclosing the specialized template. An explicit specialization whose declarator-id or class-head-name is not qualified shall be declared in the nearest enclosing namespace of the template, or, if the namespace is inline ([namespace.def]), any namespace from its enclosing namespace set. Such a declaration may also be a definition. If the declaration is not a definition, the specialization may be defined later ([namespace.memdef]).

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

来自 Columbo 的评论,

Of course a nested-name-specifier can be ::, and CC is the identifier, …?

事实并非如此,至少在这个问题的上下文中不是这样。直到 2014 版 C++ 标准,裸露的双分号没有资格作为 嵌套名称说明符 。该标准的 2003 版表示 嵌套名称说明符 采用 BNF 中的两种形式之一:

  • class-or-namespace-name :: nested-name-specifier选择
  • class-or-namespace-name :: template nested-name-specifier

没有空位 class ::CC 来适应这个规范。 2011 版本为 nested-name-specifier:

添加了相当多的 BNF
  • ::opt 类型名称 ::
  • ::opt 命名空间名称 ::
  • decltype 说明符 ::
  • 嵌套名称说明符 标识符 ::
  • 嵌套名称说明符 templateopt simple-template-id ::

这样还是没给class ::CC留下空间。 2014 版标准最终解决了这个问题,说 nested-name-specifier

之一
  • ::
  • 类型名称 ::
  • 命名空间名称 ::
  • decltype 说明符 ::
  • 嵌套名称说明符 标识符 ::
  • 嵌套名称说明符 templateopt simple-template-id ::


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

另一种观点是,这不是错误。 nested-name-specifier 在标准的 2003 和 2011 版本中的 BNF 非常清楚,因此 template<> class ::CC<int> {...} 不应编译。这是一个不幸的错误特征还是故意的特征并不重要。题中的代码从这个角度来看应该是编译不出来的

哪种观点是正确的,值得商榷。最先报告这种差异的问题没有被拒绝,这表明该报告有一些内容。另一方面,标准的两次修订都没有采取任何措施也说明了一些问题。

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