Is a class declaration allowed after a class definition? - c++

I just read this answer, and it completely puzzles me.
I was always thinking a class declaration can appear many times, and only the definition has to exist only once, like:
/*class Class {*/
class A; // (1) forward declaration
class A { // (2) definition, only once
int m;
};
class A; // (3) declaration again, legal?
class A a; // (4) declaration again, legal?
/*};*/
From the linked answer: (3) (and (4)?) is illegal if the code above is nested inside a class (definition and declarations of class A are nested inside class Class).
On cppreference, I found an example of the above, not nested:
struct s { int a; };
struct s; // does nothing (s already defined in this scope)
void g() {
struct s; // forward declaration of a new, local struct "s"
// this hides global struct s until the end of this block
s* p; // pointer to local struct s
struct s { char* p; }; // definitions of the local struct s
}
See the second line.
Question: Given that it is illegal inside a class, is my example code, and the cppreference example above, legal when not nested inside a class? Or more generally: When can a class declaration follow a definition (how is it inside namespaces for example)? If it is legal, why is there a difference?

From [basic.def]:
A declaration (Clause 7) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations.
From [class.name]:
A declaration consisting solely of class-key identifier; is either a redeclaration of the name in the current scope or a forward declaration of the identifier as a class name. It introduces the class name
into the current scope.
So it's generally legal to do this. There's just the one exception in [class.mem]:
A member shall not be declared twice in the member-specification,
except that a nested class or member class template can be declared and then later defined, and except that an enumeration can be introduced with an opaque-enum-declaration and later redeclared with an enum-specifier.
Perfectly OK in namespace scope, not allowed in class scope.
As to why? Well, this rule let's you "forward" declare all the classes you need everywhere you would typically be allowed to do so:
// a.h
struct C;
struct A {
C* c;
};
// b.h
struct C;
struct B {
C& c;
};
without having to worry about somebody actually including the full declaration and breaking everything for you:
// d.h
#include "c.h"
#include "a.h" // now C was already declared!
#include "b.h" // and here too!
struct D { ... };
This isn't so much of a concern within a class definition. It can't exactly span multiple files. So the inability to redeclare nested types doesn't actually achieve anything.

class A; This is a forward declaration of incomplete class A (legal).
class A { int m; }; This is the definition of Class A (legal).
class A; This is re-declaration of class A (legal).
class A a; This is declaration of object a of type A (legal).

Related

The first snippet below compiles, but the second doesn't. Why?

The snippet below compiles (demo):
struct A{ int i = 10; };
int main() {
struct A{ int i = 20; };
struct A;
struct A a;
}
But this doesn't:
struct A{ int i = 10; };
int main() {
// struct A{ int i = 20; };
struct A;
struct A a;
}
I can see that the answer is probably given by these paragraphs in the Standard:
[basic.lookup.elab]/2 and [basic.scope.pdecl]/7.
But I really don't know how to deduce the different behaviors shown above from these two paragraphs.
Note that in the first example the struct A is not first declared in the elaborated-type-specifier struct A;, but in the definition of struct A in main().
In the second example, the struct A is also not first declared in the elaborated-type-specifier struct A;, but in the definition of struct Ain global scope.
Each of the examples contains declarations of two different classes, both with the name A.
Let's distinguish between the classes by renaming one of them to B:
struct A{ int i = 10; };
int main() {
struct B{ int i = 20; };
struct B;
struct B b;
}
The above is semantically identical to your first example. The class A is never used.
struct A{ int i = 10; };
int main() {
struct B;
struct B b;
}
This is semantically identical to your second example. You are trying to create an object of an incomplete type, the forward-declared class B.
Renaming B back to A doesn't change anything because then the declaration of A in main shadows the declaration of the other A at global scope.
[basic.lookup.elab]/2
If the elaborated-type-specifier has no nested-name-specifier, and [...] if the elaborated-type-specifier appears in a declaration with the form:
class-key attribute-specifier-seqopt identifier ;
the elaborated-type-specifier is a declaration that introduces the class-name as described in [basic.scope.pdecl].
So struct A; is a declaration that introduces the class name in the scope of the declaration. Under no circumstances can it refer to a class declared in an outer scope.
[basic.scope.pdecl]/7
[ Note: Other forms of elaborated-type-specifier do not declare a new name [...] — end note ]
By implication, this form of elaborated-type-specifier declares a new name.
In the second example the line struct A; is a forward declaration for a struct called A in the main function's scope. This struct will be preferred to the global struct A. The next line defines a variable called a of type struct A. Since a struct A was declared in the main function's scope, that's where the compiler will search for it's definition there. It fails to find one (it's commented out). The first example compiles because there is definition in the same scope. The following example will compile however because it specified that A is in the global namespace :
struct A{ int i = 10; };
int main() {
// struct A{ int i = 20; };
struct A;
struct ::A a;
}
It doesn't compile because it can't find a definition for A.
int main() {
// struct A{ int i = 20; };
struct A;
struct A a;
}
The code above is equal to your first example, as the global A is shadowed by the local A. In the second example A doesn't have a definition. It's just a prototype. Prototypes are supposed to be placed before a piece of code that needs a definition when the definition is placed AFTER the code which needs it.
If the compliler cannot find that definition it will fail because it doesn't know what A is supposed to be (the global definition is shadowed by the local prototype, which causes it to be ignored).

How to pre-declare a typedef class in C++

For example, class A has a member of class B. In general, for the purpose of minimizing compilation dependency, we often make class A include B's pointer, and pre-declare class B in the class A's declaration. Looks like this:
//B.h
class B
{
....
};
//A.h
class B;
class A
{
B*b;
A();
...
};
//A.cpp
#include "B.h"
A::A()
{
b=new B();
...
};
But now I have a question: if the class of B is defined using typedef like this:
typedef class
{
....
}B;
The previous pre-declared method will not work in this case. How should I pre-declare the class B in A.h?
In the code typedef class { .... } B; , it is an unnamed class, with B being a type alias for the unnamed class.
It is not possible to declare an unnamed class without defining it (i.e. providing the class body); and it is not possible to declare that B is a typedef for an unnamed class.
There is no such thing as a forward declaration of a typedef. In general a typedef can declare an alias for incomplete type that is completed later, but an unnamed class cannot fill either of those roles.
Removed further advice in response to voter feedback
Note: the question used the term "pre-declare". The C++ Standard does not use the terms "pre-declare" or the more common jargon "forward-declare". In the standard's terminology, a forward declaration of a class is simply called a class declaration. This can be clarified by saying "declaration that is not a definition" if it is not clear from context already.

C++ Forward Declaring Classes Within Classes

The following simple piece of code compiles, although I don't understand why:
class C {
class B;
class A {
B getB() { return B(); }
};
class B {
};
};
int main(int, char**)
{
return 0;
}
If I then comment out the "class C" stuff, so that the forward declaration of B, the definition of A and the definition of B are no longer nested within a class, the code does not compile, since B is of an incomplete type:
main.cpp: In member function 'B A::getB()':
main.cpp:6: error: return type 'struct B' is incomplete
main.cpp:6: error: invalid use of incomplete type 'struct B'
main.cpp:3: error: forward declaration of 'struct B'
I understand what it means for a type to be incomplete, namely that it has not been defined yet and so the compiler can't possibly know how much space to allocate for it. But why is B not considered incomplete in the code above, where A and B are both declared and defined inside of C?
I believe this is a consequence of [basic.scope.class]:
The potential scope of a name declared in a class consists not only of the declarative region following the
name’s point of declaration, but also of all function bodies, default arguments, exception-specifications, and brace-or-equal-initializers of non-static data members in that class (including such things in nested
classes).
That is, the scope of the full declaration of B includes the body of the member function of the nested class:
class C {
class B; // (1)
class A {
B getB() {
return B(); // both (1) and (2) in scope here
// since (2) is the complete type declaration,
// this is perfectly fine
}
};
class B { // (2)
};
};
By comparison, if C were a namespace instead of a class, the scope of the full declaration of class B would not extend into A::getB(). The only visible declaration would be the forward-declaration of B that I labeled (1) - so B() would be the construction of an incomplete type there.
The body of an inline member function is not processed until the class definition has been fully processed.
Hence, you can use:
class A
{
class B;
B getB() { return B(); }
class B {};
};
That also allows member variables that are not yet declared to be used in inline member function definition.
class Foo
{
int getBar() { return bar; }
int bar;
};
I am guessing the same logic is extended to inline definitions of member functions of nested classes -- i.e. they are not processed until the containing class definition is completely processed.
PS I am unable to quickly locate the reference in the standard that would verify my claim.
PS 2 The answer by Barry has the reference in the standard that makes the code in the question valid.
The standard is explicit in mandating that the method's body is interpreted after the class that encloses it.
Thus at the time of evaluating the body of C::A::getB(), A, B and C are all complete types.
Besides that when I've got the need to forward declare nested classes I tend to smell some bad design in my code, the trick I use is:
// Foo.h
class Foo {
class Bar {
};
};
class Foobar : public Foo::Bar {};
// Zoo.h
/* Fwd declare */
class FooBar;

C++ class methods forward declaration

Is there any way to redeclare a class to define methods which where only declared this far?
Eg. something like:
class A
{
void a();
void b() {}
}
class A
{
void a() {}
}
instead of
class A
{
void a();
void b() {}
}
A::a() {}
The reason is I created a lot of code with methods defined inside the class defintion, without using headers. I do not had cyclic references up to now, but recently there is need to. I don't like to define bunches of methods by the Type::method syntax, as only very few methods have to be known before the latter definition of the class.
So I like somewhat like a backward declaration, declare or define only a few methods before for cyclic references and define the whole class later.
No, there is no way to redefine a class.
According to the C++ language standard the class definitions is:
class-specifier:
class-head { member-specification_opt }
The standard explicitly says that member specification should be complete within class definition:
Members of a class are data members, member functions (9.3), nested types, and enumerators. The member-specification in a class definition declares the full set of members of the class; no member can be added elsewhere.
Also the standard gives example of redefinition of class:
struct S { int a; };
struct S { int a; }; // error, double definition
is ill-formed because it defines S twice.
Unfortunately there is no way to declare the class again once it is Closed with }.
Only thing you can do is you can inherit and define the method.
class B : public A { a() {} } ;

What can I "forward declare" in C++?

I know I can do
class Foo;
and probably
struct Bar;
and global functions
bool IsValid(int iVal);
What about a typed enum? What about a typed enum within an undeclared class? What about a function with an undeclared class? What about a static member within an undeclared class? What about these within an unknown namespace? Am I missing anything else that can be forward declared?
You can forward declare
Templates, including partial specializations
Explicit specializations
Nested classes (this includes structs, "real" classes and unions)
Non-nested and local classes
Variables ("extern int a;")
Functions
If by "forward declaration" you strictly mean "declare but not define" you can also forward declare member functions. But you cannot redeclare them in their class definition once they are declared. You cannot forward-declare enumerations. I'm not sure whether I missed something.
Please note that all forward declarations listed above, except partial and explicit specializations, need to be declared using an unqualified name and that member functions and nested classes can only be declared-but-not-defined in their class definition.
class A { };
class A::B; // not legal
namespace A { }
void A::f(); // not legal
namespace A { void f(); } // legal
class B { class C; }; // legal
class B::C; // declaration-only not legal
class D { template<typename T> class E; };
template<typename T> class D::E<T*>; // legal (c.f. 14.5.4/6)