Search code examples
c++includefriendcircular-dependency

Friend function massacred by circular includes


Class A is the sole instantiator and container of instances of class B.

So it seemed like a good idea to make the constructor of class B private, and call it only by a friend function declared in class B and defined in class A.

File A.h (EDIT: included enum defined in class B)

#ifndef   A_H
#define   A_H

#include "B.h"

using namespace std;

class A
{   public:
    A();
    shared_ptr<B> CreateBInstance( const B::ENUM_STANDARD_COLORS );
    shared_ptr<B> CreateBInstance( const string );  // Custom color

    private:
    unique_ptr<map<int, shared_ptr<B>>> UPTR__BInstancesMap;
}
#endif

File B.h (EDIT: included enum defined in class B)

#ifndef   B_H
#define   B_H

#include "A.h"  // Causes chaos

using namespace std;

class B
{   public:
    enum class ENUM_STANDARD_COLORS : unsigned int
    {   ...     // ~70 standard colors
    };

    private:
    B();

    friend
    shared_ptr<B> A::CreateBInstance( const ENUM_STANDARD_COLORS );

    friend
    shared_ptr<B> A::CreateBInstance( const string );   // Custom color
}
#endif

Class A requires a full declaration (#include) of class B for shared_ptr<B>.

Class B requires some declaration of class A for the friend function. A simple forward declaration causes the error:

invalid use of incomplete type 'struc A'

at the friend function declaration.

A full declaration (#include) is far worse:

error: prototype for 'std::shared_ptr A::CreateInstance()' does not match any in class 'A'

(but it does!) at the friend function declaration.
And, because class B is now broken:

error: 'B' was not declared in this scope
error: template argument 1 is invalid

appears at every mention of B in class A, as well as in classes F and G (etc.) which #include class B for their own use of shared_ptr<B>.
And, because classes F and G are also now broken:

error: 'F' was not declared in this scope
error: template argument 1 is invalid>
error: 'G' was not declared in this scope
error: template argument 1 is invalid

appears at every mention of F and G in class A.

Unfortunately, the include guards don't prevent the circular inclusion.

Is there a way to make this friend function work? I've never achieved so many errors in one go!


Solution

  • Class A requires a full declaration (#include) of class B for shared_ptr<B>.

    No, forward declaration would be sufficient in A.h. shared_ptr (and unique_ptr) won't require class B to be complete type for return type declaration[1] and class data member declaration.

    File A.h

    #ifndef   A_H
    #define   A_H
    
    class B;  // forward declaration
    
    using namespace std;
    
    class A
    {   public:
        A();
        shared_ptr<B> CreateBInstance();
    
        private:
        unique_ptr<map<int, shared_ptr<B>>> UPTR__BInstancesMap;
    };
    #endif
    

    [1] It's also true for "regular" type T which isn't shared_ptr.