K
Ken Camann
I think I understand why the following is happening (it compiles in
both MSVC++ and gcc) I just wanted to be sure I understand the
language issues at work here. I define this very simple class in
classa.h:
template <typename T> class B; //forward declaration of B
template <typename T>
class A
{
public:
A() : x(0);
T getX() const {return x;}
void setX(T _x) {x = _x;}
changeY(B<T>& bref)
{
bref.setY(bref.getX() + 1);
}
private:
T x;
};
so I have a class "A" which stores a variable x. Similarly, I have a
symmetric class B which is almost the same but stores a variable y,
has a getY, setY, and changeX(A<T>& aref), forward declares A, etc.,
and is defined in classb.h
Now I can use these in the expected way, by #including classa and
classb in main.cpp, and increasing both x and y by 1, but indirectly
(calling changeY on A with the B instance and changeX on B with the A
instance).
My initial thought was that this would not work. This is what I
thought would happen:
1. Compiler sees #include "classa.h", includes it
2. Compiler sees "template <typename T> class B" forward declaration
3. Compiler parses declaration of class A. It's ok that it finds the
symbol B<T>& because of the forward declaration
4. Compiler parses definition of member function A<T>::changeY. This
references the names B<T>::getY and B<T>::setY. Compiler complains
because it while it recognizes reference types involving B<T> (like
B<T>* or B<T>&) because of the forward declaration, member names in
B<T> are still undefined symbols because it hasn't read the class B
declaration yet (to be included in the future, by the inclusion of
classb.h)
And yet the code worked. I have a guess as to why this is, I was
hoping someone could tell me if this is right, and if this is standard
C++ behavior:
This is OK because types are checked, but definitions are not
generated until template instantiation takes place. By the time that
happens (below both #include directives), both the declarations and
definitions of A and B have been encountered in the translation unit
main.cpp, so everything will work. This means you don't need to worry
about anything as long as you include all the h files at the top of
a .cpp translation unit; by the time your actual .cpp code is reached,
everything can see the entire definition/declaration of everything
else.
It also means that specializations of A (e.g., A<int>) cannot
reference specializations of B like B<int>, since this looks like an
instantiated type to the compiler. Instead, you must work with a
member template version of changeX, templatized with T, and accepting
generic type B<T>&. The same is true for the definition of changeY in
B<int>. At instantiation time, the correct thing will still happen,
since you will actually pass the method a B<int>&, and the B<int>
specialization has now been seen by the compiler during the #include
phase.
Is this basically correct? I tried to look this up myself in "The C++
Programming Language" but I couldn't find it (I wasn't sure what the
name of this little detail is called in C++ parlance).
Thanks,
Ken
both MSVC++ and gcc) I just wanted to be sure I understand the
language issues at work here. I define this very simple class in
classa.h:
template <typename T> class B; //forward declaration of B
template <typename T>
class A
{
public:
A() : x(0);
T getX() const {return x;}
void setX(T _x) {x = _x;}
changeY(B<T>& bref)
{
bref.setY(bref.getX() + 1);
}
private:
T x;
};
so I have a class "A" which stores a variable x. Similarly, I have a
symmetric class B which is almost the same but stores a variable y,
has a getY, setY, and changeX(A<T>& aref), forward declares A, etc.,
and is defined in classb.h
Now I can use these in the expected way, by #including classa and
classb in main.cpp, and increasing both x and y by 1, but indirectly
(calling changeY on A with the B instance and changeX on B with the A
instance).
My initial thought was that this would not work. This is what I
thought would happen:
1. Compiler sees #include "classa.h", includes it
2. Compiler sees "template <typename T> class B" forward declaration
3. Compiler parses declaration of class A. It's ok that it finds the
symbol B<T>& because of the forward declaration
4. Compiler parses definition of member function A<T>::changeY. This
references the names B<T>::getY and B<T>::setY. Compiler complains
because it while it recognizes reference types involving B<T> (like
B<T>* or B<T>&) because of the forward declaration, member names in
B<T> are still undefined symbols because it hasn't read the class B
declaration yet (to be included in the future, by the inclusion of
classb.h)
And yet the code worked. I have a guess as to why this is, I was
hoping someone could tell me if this is right, and if this is standard
C++ behavior:
This is OK because types are checked, but definitions are not
generated until template instantiation takes place. By the time that
happens (below both #include directives), both the declarations and
definitions of A and B have been encountered in the translation unit
main.cpp, so everything will work. This means you don't need to worry
about anything as long as you include all the h files at the top of
a .cpp translation unit; by the time your actual .cpp code is reached,
everything can see the entire definition/declaration of everything
else.
It also means that specializations of A (e.g., A<int>) cannot
reference specializations of B like B<int>, since this looks like an
instantiated type to the compiler. Instead, you must work with a
member template version of changeX, templatized with T, and accepting
generic type B<T>&. The same is true for the definition of changeY in
B<int>. At instantiation time, the correct thing will still happen,
since you will actually pass the method a B<int>&, and the B<int>
specialization has now been seen by the compiler during the #include
phase.
Is this basically correct? I tried to look this up myself in "The C++
Programming Language" but I couldn't find it (I wasn't sure what the
name of this little detail is called in C++ parlance).
Thanks,
Ken