Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I am mainly referring to the C++03 standard but, after a quick glance, it should also be applicable to the C++11 standard.

The following code compiled and executed successfully in 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;
}

Notice the ::CC<int> syntax to refer to the template defined in the global namespace. This is not the same as the NamespaceA::CC<int> syntax where the :: operator is preceded by something. With some other tools, I tried to parse this using the grammar strictly from the C++03 but it gave me errors and it seems to me that the standard accepts only NamespaceA::CC<int> form in the class head declaration.

On a closer look, the issue is that the class-head is defined by this grammar in the standard:

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)

And since nested-name-specifier is of the form AA::bb::..., it doesn't accept my ::CC. My question is, why the C++ standard doesn't allow the ::CC form? Is it just my incorrect interpretation of the standard grammar? Should the proper grammar looks like this:

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

Note, the above form is really used by the standard somewhere else, say, in specifying declarator-id:

declarator-id:
   id-expression
   ::(optional) nested-name-specifier(optional) class-name
like image 897
JavaMan Avatar asked May 10 '16 08:05

JavaMan


Video Answer


2 Answers

From a comment by Columbo,

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

That is not the case, at least not in the context of this question. Up until the 2014 version of the C++ standard, a bare double semicolon did not qualify as a nested-name-specifier. The 2003 version of the standard said a nested-name-specifier took on one of the two forms, in BNF:

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

There was no room for a bare class ::CC to fit into this specification. The 2011 version added quite a bit to the BNF for a nested-name-specifier:

  • ::opttype-name ::
  • ::optnamespace-name ::
  • decltype-specifier ::
  • nested-name-specifier identifier ::
  • nested-name-specifier templateoptsimple-template-id ::

This still left no room for class ::CC. The 2014 version of the standard finally addressed this by saying a nested-name-specifier is one of

  • ::
  • type-name ::
  • namespace-name ::
  • decltype-specifier ::
  • nested-name-specifier identifier ::
  • nested-name-specifier templateoptsimple-template-id ::


There are a number of ways to look at this interesting "feature". One is that this is a longstanding bug in the language specification, first identified in 2002 as issue #355. One of the jobs of a compiler vendor is to identify and patch over bugs in the language specification, and then get those bugs fixed in an upcoming release of the standard. From this point of view, template<> class ::CC<int> {...} should compile.

An alternative point of view is that this was not a bug. The BNF for a nested-name-specifier in both the 2003 and 2011 versions of the standard were quite clear, and thus template<> class ::CC<int> {...} should not compile. Whether this was an unfortunate misfeature or a deliberate feature didn't matter. The code in the question should not compile from the perspective of this point of view.

Which point of view is correct is debatable. That the issue that first reported this discrepancy was not rejected was a sign that there was some meat to that report. On the other hand, that nothing was done about it through two revisions of the standard also says something.

That said, now that the standard has been clarified, there is a bug in newer releases of GCC because even if one specifies --std=c++14, they do not allow template<> class ::CC<int> {...} to compile.

like image 141
David Hammen Avatar answered Nov 05 '22 15:11

David Hammen


In the C++ draft, the nested-name-specifier is mentioned in [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.

And it can of course also be :: according to [expr.prim.id.qual].

In your code, you're using class ::CC<int> in a template class specialization, for which [temp.expl.spec].2 also applies:

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]).

Hence, I think, using a qualified name should be okay.

like image 23
jotik Avatar answered Nov 05 '22 17:11

jotik