Declaring an object in a header when its definition is in a different header

I am new to cpp, looking at an existing project.

There are several places in the project where an object is defined in a namespace in some header file and then declared in the same namespace and used in other declarations in some other header file. Example:

MyStruct.hpp:

namespace A { struct MyStruct { int i; }; }

MyClass.hpp:

namespace A { struct MyStruct; }

namespace B {
  class MyClass {
  private:
    void foo(A::MyStruct& s);
  };
}

MyClass.cpp:

#include "MyClass.hpp"
#include "MyStruct.hpp"

namespace B {
  class MyClass {
    void foo(A::MyStruct& s) { /* ... */ }
  };
}

I might have expected there to be an #include "MyStruct.hpp" in MyClass.hpp, but that is not the case. I guess when the two header files are isolated there is not yet any relationship between the MyStruct objects in that case. Why does this not create some conflict though when the two headers are loaded together in the implementation file? I cannot, for instance, write int j; int j = 0;. Does the order of the #includes matter? And what might be the motivation for doing this?

Answer

What you are seeing is a forward declaration being used.

Since foo() takes a MyStruct by reference, MyClass.hpp doesn’t need to know the full declaration of MyStruct in order to declare foo(), it only needs to know that MyStruct exists somewhere. The linker will match them up later.

MyClass.cpp, on the other hand, needs to know the full declaration of MyStruct in order for foo()‘s implementation to access the contents of its s parameter. So MyStruct.hpp is used there instead.

This also means that if the contents of MyStruct.hpp are ever modified, any source files using MyClass.hpp don’t have to be recompiled since the forward declaration of MyStruct won’t change (unless the namespace is changed, that is). Only files that are using MyStruct.hpp will need recompiling, like MyClass.cpp.