How to properly avoid circular dependency with friend method in C++?

99 Views Asked by At

I have 2 classes: MyString and LongNumber. And the LongNumber class has a Foo method that has access to the private fields of the MyString class, that is to say, the method Foo is a friend to the class MyString. So I wrote some code here:

// File: MyString.h

- - - - - - - - - - - - - -

#pragma once
// default includes with angular brackets

class LongNumber; // forward declaration

class MyString
{
    char* symbols;
    size_t capacity;
    size_t length;

    friend void LongNumber::Foo(MyString& str); // <-- error here

    // Methods
}
// File: MyString.cpp

- - - - - - - - - - - - - -

// including "MyString.h" and "LongNumber.h" in .cpp to avoid circular dependency
#include "MyString.h"
#include "LongNumber.h"
// default includes with angular brackets

// Implementations
// File: LongNumber.h

- - - - - - - - - - - - - -

#pragma once
// default includes with angular brackets

class MyString; // forward declaration

class LongNumber
{
    uint8_t sign;
    MyString digits;
    void Foo(MyString& str); // supposed to be a friend to the class MyString

    // Methods
}
// File: LongNumber.cpp

- - - - - - - - - - - - - -

// including "MyString.h" and "LongNumber.h" in .cpp to avoid circular dependency
#include "MyString.h"
#include "LongNumber.h"
// default includes with angular brackets

void LongNumber::Foo(MyString& str) // supposed to be a friend to the class MyString
{
    // some code
}

// other implementations
File: Main.cpp

- - - - - - - - - - - - - -

#include <iostream>
#include "MyString.h"
#include "LongNumber.h"

// #include "LongNumber.h"
// #include "MyString.h"
// reversed order won't help either

int main() {} // empty

I have read about avoiding circular dependencies in C++. They advise you to forward declare the class in the .h file, and to move all the include's to the .cpp file. I have done so, but this won't work for me.

When I compile this, I get: ...MyString.h(11,26): error C2027: use of undefined type 'LongNumber'

The line of code which causes this error is: friend void LongNumber::Foo(MyString& str);

The compiler does not care that I forward declared the class LongNumber. It almost seems that it does not see my other files in some sense, because if I modify the MyString.h file like this:

// File: MyString.h

- - - - - - - - - - - - - -

#pragma once
// default includes with angular brackets

class UnknownClass; // forward declaration, deliberately wrote a name that does not exist

class MyString
{
    char* symbols;
    size_t capacity;
    size_t length;

    friend void UnknownClass::Foo(MyString& str); // <-- error here

    // Methods
}

The error is the very same: ...MyString.h(11,28): error C2027: use of undefined type 'UnknownClass'

I feel like the only way to let the compiler know that the class LongNumber exists and is completed is to put #include "LongNumber.h" into the header file, but that's not allowed, because it would create a circular dependency.

Is what I am trying to accomplish possible?

1

There are 1 best solutions below

0
463035818_is_not_an_ai On

You cannot refer to a method of an incomplete class by its name. Make UnknownClass a KnownClass:

// KnownClass.h
struct MyString;

struct KnownClass {
    void foo(const MyString&);
};

// MyString.h
struct MyString {
    friend void KnownClass::foo(const MyString&);
};

// KnownClass.cpp

void KnownClass::foo(const MyString&) {
    // implementation
}

Circular dependency yes, but its not an issue, because void KnownClass::foo(const MyString&); only needs a declaration of MyString, but it doesnt need MyString to be complete.