Reputation: 5955
I am creating a header-only library. In this library I would like to already use a class that is defined later in the header. For example:
class A {
public:
int i;
A(int x) : i(x) {};
B func(B inp) {
B out(i*inp.j);
return out;
};
};
class B {
public:
int j;
B(int x) : j(x) {};
A other(A inp) {
A out(j*inp.i);
return out;
};
};
Naturally my compiler does not like this. It produces the first error on the line on which func
is defined:
error: unknown type name 'B'
Is there a way to overcome this?
Upvotes: 2
Views: 353
Reputation: 45414
Using a reference or pointer requires a declaration of the corresponding type. Using an object (instance) or any member requires the full definition or the corresponding type. A declaration is
class A; // declares: there is a class called 'A'
while a definition is
class A { // (re-)declares A and defines its layout and members
/* details */
};
A declaration can precede a definition (but does not have to for the definition to be valid). This immediately gives the following solution.
Order the class definitions such that later defined classes only use objects and members of earlier defined class in their interface. For example
class A {
int i;
public:
A(int x) : i(x) {}
};
class B {
A a; // object of type A: okay, since A is defined
public:
B(int i) : a(i) {}
int foo() const {
return a.i; // member of a: okay, since A::i is defined
}
};
Classes of which references or pointers are used in any earlier declared class can be forward declared (not defined) before any class definition. For example
class B; // forward declaration of B
class A {
int i;
public:
A(int x) : i(x) {}
A(B*); // pointer to B okay, since B is declared
// cannot define A::A(B*): it depends on definition of B
};
class B {
int x;
A a; // object of type A: okay, since A is defined
public:
B(int i) : x(i+i), a(i) {}
B() : a(this);
}
Define any implementation details that depend on the definition of classes not known at the time of declaration.
inline A::A(B*b) // either inline or in a source file
: i(b->x/2) // using member B::x okay: B is defined
Upvotes: 4
Reputation: 122133
Not saying that this would be the way to go, but if a little misuse of templates is fine with you, you could do something like this (same example but removed irrelevant stuff and added B using A as you mentioned):
template <typename X>
struct A {
X func(X inp) { return X(/*...*/); }
};
struct B {
public:
A<B> func(A<B> inp) { return A<B>(); }
};
As I said, I wouldnt really do this just to avoid a forward declaration. On the other hand, you should reconsider if you really need the circular dependency. If two classes depend too much on each other, you cant touch one without having to check if the other didnt break. Thats not nice and if possible I would avoid it.
Upvotes: 1
Reputation: 409136
One way is to forward declare the class B
, and move the function definition out of the class.
Normally one would put the definition in a source file, but since it's a header-only library you can instead mark the function definition as inline
and you can put it inside the header file itself, but after the definition of the B
class of course.
Another and more simpler way, if B
doesn't depend on A
, is to move the definition of B
to be above the definition of A
in the header file.
Upvotes: 4