A Class Name Introduced Inside a Class Is Not Treated as a Nested Class Name

A class name introduced inside a class is not treated as a nested class name

g++'s behavior here is entirely correct. This is specified in §3.3.2 [basic.scope.pdecl]/p7 of the standard:

The point of declaration of a class first declared in an
elaborated-type-specifier is as follows:

  • for a declaration of the form

    class-key attribute-specifier-seqopt identifier ;
    the identifier is declared to be a class-name in the scope that contains
    the declaration, otherwise
  • for an elaborated-type-specifier of the form

    class-key identifier
    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a
    function defined in namespace scope, the identifier is declared as a
    class-name in the namespace that contains the declaration; otherwise, except as a friend declaration*, the identifier is declared
    in the smallest namespace or block scope that contains the
    declaration.

Note that in the second case the declaration is always placed in a namespace or block scope, never a class scope, so it can never declare a nested class. Also, in the second case, a lookup will be performed and only if a previously declared type-name is not found will the elaborated-type-specifier be taken to declare a new name (§3.4.4 [basic.lookup.elab]/p2, §9.1 [class.name]/p3 note).


* Friend declarations have their own weird rules. The names first declared in friend declarations are still placed in namespace (for non-local classes) or block (for local classes) scope, but they are not made visible for most name lookup (except for ADL in case of functions) until they are also declared in the scope containing them. The rules for non-local classes are specified in §7.3.1.2 [namespace.memdef]/p3:

If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the
name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.

The rules for local classes are specified in §11.3 [class.friend]/p11:

If a friend declaration appears in a local class (9.8) and the name specified is an unqualified name, a prior declaration is looked up without considering scopes that are outside the innermost enclosing non-class scope. [...] For a friend class declaration, if there is no prior declaration, the class that is specified belongs to the innermost enclosing non-class scope, but if it is subsequently referenced, its name is not found by name lookup until a matching declaration is provided in the innermost enclosing non-class scope.

Java inner class and static nested class

From the Java Tutorial:

Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are simply called static nested classes. Non-static nested classes are called inner classes.

Static nested classes are accessed using the enclosing class name:

OuterClass.StaticNestedClass

For example, to create an object for the static nested class, use this syntax:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

Objects that are instances of an inner class exist within an instance of the outer class. Consider the following classes:

class OuterClass {
...
class InnerClass {
...
}
}

An instance of InnerClass can exist only within an instance of OuterClass and has direct access to the methods and fields of its enclosing instance.

To instantiate an inner class, you must first instantiate the outer class. Then, create the inner object within the outer object with this syntax:

OuterClass outerObject = new OuterClass()
OuterClass.InnerClass innerObject = outerObject.new InnerClass();

see: Java Tutorial - Nested Classes

For completeness note that there is also such a thing as an inner class without an enclosing instance:

class A {
int t() { return 1; }
static A a = new A() { int t() { return 2; } };
}

Here, new A() { ... } is an inner class defined in a static context and does not have an enclosing instance.

Is a nested class name be considered as a current instantiation when it is used in a enclosing class template

Is a nested class name be considered as a current instantiation when it is used in a enclosing class template?

No.

However, as with many other passages of the standard, paragraph [temp.dep.type]/1.1 is indeed a mouthful:

A name refers to the current instantiation if it is

  • (1.1) in the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, the injected-class-name of the class template or nested class, [...]

and we may need to break it down into the different cases to see that it does not apply to a nested-class-name when in the definition of class template where, in turn, the nested class is defined:

A name refers to the current instantiation if it is,

  • in the definition of a class template, the injected-class-name of the class template ["or nested class" does not apply for this case]
  • in the definition of a nested class of a class template, the injected-class-name of the class template or [the] nested class
  • in the definition of a member of a class template, the injected-class-name of the class template ["or nested class" does
    not apply for this case]
  • in the definition of a member of a nested class of a class template, the injected-class-name of the class template or [the] nested class

where I have emphasized the connection of the "if it is in the definition of a X (of a Y), the injected-class-name of the X (or [the] Y)". Noting particularly that when the definition is of a class template (when X is class template), there is no "a nested class" to refer back to at the latter part, when describing the "injected-class-name of the ...".

Conversely, there is no way interpret [temp.dep.type]/1.1 as

" in the definition of a class template, the injected-class-name of the class template or any nested class of the class template "

Thus, in your example, #2, we are in the definition of a class template (namely A), meaning only the name of the class template itself, namely A, refers to the current instantiation.



Do you agree the sentence is vague?

Given the above, I would not say that it is vague (as this would imply some ambiguity, which I cannot see), but possible needlessly complex. It could arguably be clearer if [temp.dep.type]/1.1 was split into two separate paragraphs:

A name refers to the current instantiation if it is

  • (1.1a) in the definition of a class template, or a member of a class template, the injected-class-name of the class template,
  • (1.1b) in the definition of a nested class of a class template, or a member of a nested class of a class template, the injected-class-name of the class template or nested class, [...]

Why can't inner class and outer class have same name?

Class X already has a member named X, referring to itself. This is known as an injected class name. It's then invalid to add another member with the same name.

Cannot access inherited property if class has nested class of same name

The problems is due to name collision between Helper class (type) and Helper property. Try this

public interface IHelper
{
void DoHelp();
}

public abstract class ClassWithHelperBase<THelper> where THelper : IHelper
{
public THelper Helper { get; set; }
}

public class ClassWithHelper : ClassWithHelperBase<ClassWithHelper.CHelper>
{
// use a nested class, since there will be n classes deriving from ClassWithHelper and giving each helper a readable name (in this example ClassWithHelperHelper) is ugly
public class CHelper : IHelper
{
public static void SomeStaticMethod() {}
public void DoHelp() { }
}
}

public class Test
{
private ClassWithHelper myClass;

public void DoTest() {
myClass.Helper.DoHelp();
ClassWithHelper.CHelper.SomeStaticMethod();
}
}

Here I renamed Helper class to the CHelper, so compiler can now distinguish class and property and thus the line myClass.Helper.DoHelp(); now works without cast.

If a "do not rename nested class" requirement is absolutely mandatory, then the problem may be also solved by renaming the Helper property in the base class to avoid name collision. However, I can't imagine better name for the property.

Unfortunately, for the static method, you can't reference myClass instance. So, you will need reference the whole type.

C++ 11 class keyword

That is known as an elaborated type specifier and is generally only necessary when your class name is "shadowed" or "hidden" and you need to be explicit.

class T
{
};

// for the love of god don't do this
T T;
T T2;

If your compiler is smart, it will give you these warnings:

main.cpp:15:5: error: must use 'class' tag to refer to type 'T' in this scope
T T2;
^
class
main.cpp:14:7: note: class 'T' is hidden by a non-type declaration of 'T' here
T T;
^

If your class is not previously defined, it will also act as a forward declaration.

For example:

template <typename T>
void Method()
{
using type = typename T::value_type;
}

Method<class T>(); // T is a forward declaration, but we have not defined it yet,
// error.

Otherwise it's not necessary.

class T
{
public:
using value_type = int;
};

// method implementation...

Method<T>();
Method<class T>(); // class is redundant here


Related Topics



Leave a reply



Submit