Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a forward declaration in a class not a forward declaration to an inner class? C++ [duplicate]

Tags:

c++

c++11

g++

Take these class definitions:

Class definition 1:

struct A 
{
   struct B* m_b;
};

Class definition 2:

struct A 
{
   struct B;
   B* m_b;
};

Both the class defintions should declare B as a nested class. At least, that's what I thought by reading the following from the draft C++11 standard:

9.1/2 A class declaration introduces the class name into the scope where it is declared and hides any class, variable, function, or other declaration of that name in an enclosing scope (3.3). If a class name is declared in a scope where a variable, function, or enumerator of the same name is also declared, then when both declarations are in scope, the class can be referred to only using an elaborated-type-specifier `

However, g++ 4.8.2 treats them differently. In the first definition, it treats B as a class that is a peer to A.

The following program is built successfully:

struct A 
{
   struct B* m_b;
};

void myfun(const B& b )
{
}

int main()
{
   A a;
   myfun(*a.m_b);
}

while the following program does not:

struct A 
{
   struct B;
   B* m_b;
};

void myfun(const B& b )
{
}

int main()
{
   A a;
   myfun(*a.m_b);
}

I understand why the second program does not compile but I don't understand why the first program is built successfully.

Am I missing something in the interpretation of the standard?

Is g++ 4.8.2 correct in compiling the first program?

like image 865
R Sahu Avatar asked Jun 26 '14 06:06

R Sahu


People also ask

What is a forward class declaration?

In C++, Forward declarations are usually used for Classes. In this, the class is pre-defined before its use so that it can be called and used by other classes that are defined before this.

Can you forward declare a nested class?

You cannot forward declare a nested structure outside the container. You can only forward declare it within the container.

What is the point of forward declaration?

A forward declaration allows us to tell the compiler about the existence of an identifier before actually defining the identifier. In the case of functions, this allows us to tell the compiler about the existence of a function before we define the function's body.

What is forward declaration in Objective C?

In Objective-C, classes and protocols can be forward-declared like this: @class MyClass; @protocol MyProtocol; In Objective-C, classes and protocols can be forward-declared if you only need to use them as part of an object pointer type, e.g. MyClass * or id<MyProtocol>.


1 Answers

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.

like image 196
T.C. Avatar answered Sep 29 '22 10:09

T.C.