0

I ran into circular dependency when designing two classes, let's call them Foo and Baz, that both contain function templates. To solve it I removed the include statements from the top of both the headers, and then used .inl-files to implement the function templates, that I then included at the bottom of the class headers. The headers could then simply have forward declarations and the .inl files could do the including, making sure that both classes were defined before any member functions were implemented.

Simple enough, right? Well, not quite.

Foo needs to have one of Baz's member functions as a friend (for the purpose of this question, assume that this is a requirement and not something that I can change). Meaning I can't use a forward declaration of Baz to declare the friendship, I need the actual header where the Baz is defined.

class Baz;

class Foo
{
    template<typename T>
    friend void Baz::templateFunc(Foo&); // Error
};

After a long series of attempted solutions, I've come up with the following:

baz.h

#pragma once

class Foo;

class Baz
{
public:
    template<typename T>
    void templateFunc(Foo& f);
};

#ifndef FOO_H // If this header was not included by foo.h...
#include "baz.inl" // ...then include the inline file
#endif

baz.inl

#define BAZ_INL

#include "foo.h"

template<typename T>
void Baz::templateFunc(Foo& f)
{
    ...
}

foo.h

#ifndef FOO_H
#define FOO_H

#include "baz.h"

class Foo
{
    template<typename T>
    friend void Baz::templateFunc(Foo&);

    ...
    // Some function templates that are implemented in foo.inl
};
#include "foo.inl"

#ifndef BAZ_INL
#include "baz.inl"
#endif

#endif

(foo.inl is not important here)

This solution works but I'm not very satisfied with it. First of all, it looks very messy to me. Someone going through these headers would probably be confused as to why baz.h checks if another header has been defined before implementing its functions and why foo.h is including the implementation of baz.h. It seems like a busy solution just for satisfying a friend declaration.

Is there a simpler solution for this type of circular dependency?

JensB
  • 839
  • 4
  • 19
  • 2
    do you really need a friend function? They are generally regarded as a [code smell](https://en.wikipedia.org/wiki/Code_smell) – Alan Birtles Dec 02 '21 at 22:17
  • 2
    If you have classes this tightly bound, consider putting them both in the same file. That way it is easy to place the member function definitions in an order where they all make sense. – user4581301 Dec 02 '21 at 22:18
  • https://stackoverflow.com/questions/6310720/declare-a-member-function-of-a-forward-declared-class-as-friend – Hans Passant Dec 02 '21 at 22:47

0 Answers0