I’m getting this error when dealing with a number of classes including each other:
error: expected class-name before '{' token
I see what is going on, but I do not know how to properly correct it. Here is an abstracted version of the code:
A.h
#ifndef A_H_ #define A_H_ #include 'K.h' class A { public: A(); }; #endif /*A_H_*/
A.cpp
#include 'A.h' A::A() {}
B.h
#ifndef B_H_ #define B_H_ #include 'A.h' class B : public A { // error: expected class-name before '{' token public: B(); }; #endif /*B_H_*/
B.cpp
#include 'B.h' B::B() : A() {}
J.h
#ifndef J_H_ #define J_H_ #include 'B.h' class J { public: J(); }; #endif /*J_H_*/
J.cpp
#include 'J.h' J::J() {}
K.h
#ifndef K_H_ #define K_H_ #include 'J.h' class K : public J { // error: expected class-name before '{' token public: K(); }; #endif /*K_H_*/
K.cpp
#include 'K.h' K::K() : J() {}
main.cpp
#include 'A.h' int main() { return 0; }
Starting in main.cpp, I can determine that this is what the compiler sees:
#include 'A.h' #ifndef A_H_ #define A_H_ #include 'K.h' #ifndef K_H_ #define K_H_ #include 'J.h' #ifndef J_H_ #define J_H_ #include 'B.h' #ifndef B_H_ #define B_H_ #include 'A.h' class B : public A { // error: expected class-name before '{' token
So, A‘s definition is not complete when we get to B. I’ve been told that sometimes you need to use a forward declaration and then move the #include statement into the .cpp file, but I’m not having any luck with that. If I try anything like that, I simply get the additional error:
error: forward declaration of 'struct ClassName'
I think maybe I’m just not doing things in the right places. Can someone please show me how to get this code to compile? Thank you very much!
Edit: I want to point out that this is just abstracted version of the real code. I realize that there are no references to K in A or B in J, but there are in the real code and I feel that they’re completely necessary. Perhaps if I give a brief description of the real classes, someone can help me restructure or fix my code.
Class A is an abstract node class that acts as an interface for nodes in a graph. Class B is one of what will be a number of different implementations of A. In the same manner, class J is an abstract Visitor class and K is the corresponding implementation. Here is the code with a little more context:
A.h (Abstract Node)
#ifndef A_H_ #define A_H_ #include 'K.h' class K; class A { public: A(); virtual void accept(const K&) const = 0; }; #endif /*A_H_*/
A.cpp
#include 'A.h' A::A() {}
B.h (Concrete Node)
#ifndef B_H_ #define B_H_ #include 'A.h' class K; class B : public A { // error: expected class-name before '{' token public: B(); virtual void accept(const K&) const; }; #endif /*B_H_*/
B.cpp
#include 'B.h' B::B() : A() {} void B::accept(const K& k) const { k.visit(this); }
J.h (Abstract Visitor)
#ifndef J_H_ #define J_H_ #include 'B.h' class B; class J { public: J(); virtual void visit(const B*) const = 0; }; #endif /*J_H_*/
J.cpp
#include 'J.h' J::J() {}
K.h (Concrete Visitor)
#ifndef K_H_ #define K_H_ #include 'J.h' class B; class K : public J { // error: expected class-name before '{' token public: K(); virtual void visit(const B*) const; }; #endif /*K_H_*/
K.cpp
#include 'K.h' K::K() : J() {} void K::visit(const B*) const {};
main.cpp
#include 'A.h' int main() { return 0; }
I had to add some forward declarations to make some additional errors that appeared (when I added detail) go away. Some of them may not be necessary or correct.
Circular inclusions do not work.
Try to keep inclusions to a strict minimum. If you do that, you’ll either be fine altogether, or you’ll discover problems in your design. In your case, i don’t see anything wrong with your design.
When defining class K, you’re only using a pointer to an object of type B. That does not require B to be defined (as in ‘include the header file’), only to be declared (forward declaration is fine). So, in your case, removing inclusion to header ‘B.h’ replaced by ‘class B;’ is sufficient. (the same goes for class J)