Both of those codes are invalid (only the last one is valid), but your compiler (which is not conforming) only diagnoses one. As another answer says, this uses the injected class name. A class S
is considered to have a member name S
denoting that same class. For example (notice the "class" keyword before S::S
in the first example is necessary to force a reference to the injected class name, instead of the default constructor):
class S { };
class S::S object; // creates an S object
class X : S::S::S::S { }; // derives from class S
Class templates also have an injected class name. Like the injected class name, it is inherited to derived classes, and thus ST<int>
is ill-formed because it uses that injected class name, which however is not accessible. If you use GCC less 4.5, it may have something to do with a change introduced with GCC4.5:
G++ now implements DR 176. Previously G++ did not support using the injected-class-name of a template base class as a type name, and lookup of the name found the declaration of the template in the enclosing scope. Now lookup of the name finds the injected-class-name, which can be used either as a type or as a template, depending on whether or not the name is followed by a template argument list. As a result of this change, some code that was previously accepted may be ill-formed because
- The injected-class-name is not accessible because it's from a private base, or
- The injected-class-name cannot be used as an argument for a template template parameter.
In either of these cases, the code can be fixed by adding a nested-name-specifier to explicitly name the template. The first can be worked around with -fno-access-control; the second is only rejected with -pedantic.
To have a bit more fun with injected class names - notice that the injected class name is not equivalent to a typedef as one might think first. The injected class name is a class-name, but is not classified as a typedef-name, which means it can be hidden by function, object or enumerator names:
// valid, the data-member hides the injected class name
struct S { int S; };
To refer to the injected class name you can say class S::S
(likewise, in a base-class list, non-type names are ignored thus you don't need special pre-cautions there), but a simple lookup to S::S
will refer to the data-member.