I have two template classes which both depend on each other. As an example consider
template< typename > struct B;
template< typename T > struct A {
void doSomething() {
B<T> b{t};
b->doSomething();
// do some more
}
T t;
};
template< typename T > struct B {
void doSomething() {
// do something
}
A<T> createA() {
// Something
}
};
Based on my understanding of this, two-phase-lookup renders my snippet above ill-formed, because A
's implementation requires type B
to be complete, but at the time A
is implemented here, B
is still incomplete.
If these were not template classes, the problem would vanish as we would only require B
in A.cpp
. Therefore, when B.h
includes A.h
(because B
should actually require A
to be complete at the time it declares createA
).
However, since these are templates, we have to define everything in header files, which seems to makes this a lot more complicated.
How are such situations typically resolved?
CodePudding user response:
Anything that depends on T
is a dependent type, and as such is looked up during the second phase.
You only need to forward-declare A
before B
, then you can use A<T>
safely.
Of course both A
and B
need to be fully defined at the time you actually instantiate them.
template< typename T > struct A;
template< typename T > struct B {
void doSomething() {}
A<T> createA() { return {t}; }; // parsing deferred until 2nd phase
T t;
};
template< typename T > struct A {
void doSomething() {}
T t;
};
int main() {
A<int> a{5};
a.doSomething();
B<int> b{12};
A<int> c = b.createA();
}
CodePudding user response:
Thanks to @463035818_is_not_a_number's comment I think I found the solution for my problem. Instead of only providing A.hpp
and B.hpp
, we can also define A_fwd.hpp
and B_fwd.hpp
.
This allows us to also separate declaration and definition of the template's functions by first declaring the template classes in the respective _fwd.hpp
files and then putting the actual definition into the regular *.hpp
files (after having included the respective _fwd.hpp
).
For my example this could look like this:
A_fwd.hpp
template< typename T > struct A {
void doSomething();
T t;
};
B_fwd.hpp
template< typename T > struct B {
void doSomething();
A<T> createA();
T t;
};
A.hpp
#include "A_fwd.pp"
template< typename T > void A<T>::doSomething() {
B<T> b{t};
b.doSomething();
// do some more
}
B.hpp
#include "B_fwd.hpp"
template< typename T > void B<T>::doSomething() {
// something
}
template< typename T > A<T> B<T>::createA() {
return { t };
}
While this increases the amount of required typing, it essentially gives us the same rules to play by as for non-template classes, which are implemented as separate declarations (header) and definition (cpp-file).