[c++] Resolve build errors due to circular dependency amongst classes

I'm late answering this, but there's not one reasonable answer to date, despite being a popular question with highly upvoted answers....

Best practice: forward declaration headers

As illustrated by the Standard library's <iosfwd> header, the proper way to provide forward declarations for others is to have a forward declaration header. For example:

a.fwd.h:

#pragma once
class A;

a.h:

#pragma once
#include "a.fwd.h"
#include "b.fwd.h"

class A
{
  public:
    void f(B*);
};

b.fwd.h:

#pragma once
class B;

b.h:

#pragma once
#include "b.fwd.h"
#include "a.fwd.h"

class B
{
  public:
    void f(A*);
};

The maintainers of the A and B libraries should each be responsible for keeping their forward declaration headers in sync with their headers and implementation files, so - for example - if the maintainer of "B" comes along and rewrites the code to be...

b.fwd.h:

template <typename T> class Basic_B;
typedef Basic_B<char> B;

b.h:

template <typename T>
class Basic_B
{
    ...class definition...
};
typedef Basic_B<char> B;

...then recompilation of the code for "A" will be triggered by the changes to the included b.fwd.h and should complete cleanly.


Poor but common practice: forward declare stuff in other libs

Say - instead of using a forward declaration header as explained above - code in a.h or a.cc instead forward-declares class B; itself:

  • if a.h or a.cc did include b.h later:
    • compilation of A will terminate with an error once it gets to the conflicting declaration/definition of B (i.e. the above change to B broke A and any other clients abusing forward declarations, instead of working transparently).
  • otherwise (if A didn't eventually include b.h - possible if A just stores/passes around Bs by pointer and/or reference)
    • build tools relying on #include analysis and changed file timestamps won't rebuild A (and its further-dependent code) after the change to B, causing errors at link time or run time. If B is distributed as a runtime loaded DLL, code in "A" may fail to find the differently-mangled symbols at runtime, which may or may not be handled well enough to trigger orderly shutdown or acceptably reduced functionality.

If A's code has template specialisations / "traits" for the old B, they won't take effect.

Examples related to c++

Method Call Chaining; returning a pointer vs a reference? How can I tell if an algorithm is efficient? Difference between opening a file in binary vs text How can compare-and-swap be used for a wait-free mutual exclusion for any shared data structure? Install Qt on Ubuntu #include errors detected in vscode Cannot open include file: 'stdio.h' - Visual Studio Community 2017 - C++ Error How to fix the error "Windows SDK version 8.1" was not found? Visual Studio 2017 errors on standard headers How do I check if a Key is pressed on C++

Examples related to compiler-errors

intellij idea - Error: java: invalid source release 1.9 Error:Execution failed for task ':app:transformClassesWithJarMergingForDebug' Deserialize JSON with Jackson into Polymorphic Types - A Complete Example is giving me a compile error Android java.exe finished with non-zero exit value 1 error: expected primary-expression before ')' token (C) What does "collect2: error: ld returned 1 exit status" mean? Python3: ImportError: No module named '_ctypes' when using Value from module multiprocessing Maven error :Perhaps you are running on a JRE rather than a JDK? What does a "Cannot find symbol" or "Cannot resolve symbol" error mean? Operator overloading ==, !=, Equals

Examples related to circular-dependency

Python circular importing? ImportError: Cannot import name X Circular (or cyclic) imports in Python Resolve build errors due to circular dependency amongst classes

Examples related to c++-faq

What are the new features in C++17? Why should I use a pointer rather than the object itself? Why is enum class preferred over plain enum? gcc/g++: "No such file or directory" What is an undefined reference/unresolved external symbol error and how do I fix it? When is std::weak_ptr useful? What XML parser should I use in C++? What is a lambda expression in C++11? Why should C++ programmers minimize use of 'new'? Iterator invalidation rules