7

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
KennyLog_ins
  • 157
  • 1
  • 15
JavaMan
  • 4,954
  • 4
  • 41
  • 69
  • Of course a nested-name-specifier can be `::`, and `CC` is the identifier, …? – Columbo May 10 '16 at 08:25
  • 2
    See also: http://stackoverflow.com/q/2781339/256138 – rubenvb May 10 '16 at 08:27
  • I notice that C++11 added a leading '::' to the nested-name-specifier while the c++03 standard requires a class-or-namespace-name before the '::'. Probably a 'bug' in C++03 – JavaMan May 10 '16 at 08:32
  • @JavaMan the [link](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#355) inside the other question (search for "355.", the # is nonfunctional) seems to suggest exactly that. Unclear is if it was fixed in C++11 or 14 or not yet even. – rubenvb May 10 '16 at 08:34
  • 2
    @Columbo http://wg21.link/cwg1411 – T.C. May 10 '16 at 10:38
  • @JavaMan -- c++11 did not add a bare `::` as a *nested-name-specifier*. That longstanding bug in the language (first identified in 2002) was not patched until C++14. – David Hammen Jul 04 '16 at 18:33

2 Answers2

3

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:

  • ::opt type-name ::
  • ::opt namespace-name ::
  • decltype-specifier ::
  • nested-name-specifier identifier ::
  • nested-name-specifier templateopt simple-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 templateopt simple-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.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
2

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.

jotik
  • 17,044
  • 13
  • 58
  • 123
  • Yet [GCC](http://coliru.stacked-crooked.com/a/fcf850b84bd184c3) gives an error, although [Clang](http://coliru.stacked-crooked.com/a/6e6bf3157c751ec6) handles it gracefully. Looks like a GCC bug. – rubenvb May 10 '16 at 08:26
  • @rubenvb Yes I also noted that with GCC 4.9 and 5.3.0, and with Clang 3.7.1. – jotik May 10 '16 at 08:28
  • This answer pertains to the 2014 version of the standard, but not to the 2011 and earlier versions. Issue #355 was voted into the WP in March, 2011 as paper N3259. This was after paper N3242 had already been approved as the draft of the C++ 20011 standard. – David Hammen Jul 07 '16 at 08:53