0

i have a big problem, and i don't know if i miss some obvious things or what, however i can't find my mistake. I have Class SPN, A and B. I overloaded 2 times operator=. I want to pass A type or B type as parameter.

void SPN::operator=(A*& R)
{
    (*R)(*this);
}
void SPN::operator=(B*& R)
{
    (*R)(*this);
}

and it doesnt throw any error. But if i try make operator() in class B or A then with parameter SPN like below:

void A::operator()(SPN*& spn)
{
    //todo
}

It throws error that SPN doesn't name a type. I can't even create class SPN object in A or B class. Maybe it is not how objective programming works, so i want to get it, why I can't do that.

There are my codes: A.h (B is the same)

#pragma once
#include "SPN.h"

class A 
{
public:
    SPN temp;                   <<it throws error: 'SPN' has not been delcared
    void operator()(SPN*& spn); <<it throws error: 'SPN' has not been delcared
};

SPN.h

#pragma once
#include "A.h"
#include "B.h"
#include <random>
#include <iostream>

class SPN
{
public:
    friend class A;
    friend class B;
    A* a;
    B* b;
    void operator=(A*& R);
    void operator=(B*& R);
};

To summarize my question is: Why it throws error, that type SPN doesn't name a type (in A and B classes) but for SPN it works fine (in operators)

Kawson
  • 138
  • 1
  • 10
  • 1
    Do `Infection.h` or `Recovery.h` contain `#include "A.h"`, by any chance? – Nathan Pierson Apr 15 '21 at 15:07
  • [edit] to quote the full compiler error including implicated line/column. – underscore_d Apr 15 '21 at 15:08
  • I'm not sure what `*&` is supposed to mean... a reference to a pointer? (making sure that you can pass only an lvalue to the = operator?) Otherwise, this might be an include problem – somerandomdev49 Apr 15 '21 at 15:08
  • 1
    operators are expected to operate on objects not on pointers. `operator=` is not expected to modify the right operand. You can do the most weirdest things, but I am not sure if you really want `A*&`. Anyhow, to help with the error please include a [mcve] and the complete error message in the question – 463035818_is_not_an_ai Apr 15 '21 at 15:10
  • Definitely smells of include cycle as @NathanPierson hints. – Jeffrey Apr 15 '21 at 15:10
  • Sorry, Infection.h is A.h i just renamed class from my project to be easier. A is Infection – Kawson Apr 15 '21 at 15:11
  • 2
    [This dupe](https://stackoverflow.com/questions/625799/resolve-build-errors-due-to-circular-dependency-amongst-classes) might be helpful, then. – Nathan Pierson Apr 15 '21 at 15:12
  • In general, avoid including files in headers. Include in implementation, and in headers use forward declarations. – Artur Pasymowski Apr 15 '21 at 15:15
  • @largest_prime_is_463035818 I edited and said 1:1 error message. But what do you want more if i said simple "error that SPN doesn't name a type." ? It is only error i've got... – Kawson Apr 15 '21 at 15:16
  • @somerandomdev49 it was just mistake in post text, infection=A, I corrected it – Kawson Apr 15 '21 at 15:17
  • Does this answer your question? [Resolve build errors due to circular dependency amongst classes](https://stackoverflow.com/questions/625799/resolve-build-errors-due-to-circular-dependency-amongst-classes) – Stephen Newell Apr 15 '21 at 15:19

2 Answers2

2

Since A.h and B.h get included in SPN.h and SPN.h gets included in A.h and B.h, there is a circular dependency. What I would do is remove the A.h and B.h includes from SPN.h You may need to forward declare A and B in SPN.h though (since it won't actually be including the file, it'll have the same error you're seeing now):

#pragma once
#include <random>
#include <iostream>

// forward declarations 
class A;
class B;

class SPN
{
public:
    friend class A;
    friend class B;
    A* a;
    B* b;
    void operator=(A*& R);
    void operator=(B*& R);
};

Since you're only using pointers this should work. You'll likely need to include A.h and B.h in the source (SPN.cpp, SPN.cc, etc) file to get things compiling. Hopefully that sorts the issue out!

Also, like a few of the commenters have said, A*& is a bit funky - but that might be left to a different question.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
mattlangford
  • 1,260
  • 1
  • 8
  • 12
  • Doesn't `friend class A;` already have the effect of forward-declaring `A`? – Nathan Pierson Apr 15 '21 at 15:16
  • I tested in Godbolt: https://godbolt.org/z/4dTv9bfb8 and it seems like it still needs the forward declaration. – mattlangford Apr 15 '21 at 15:17
  • Is including header files in .cpp file fine? I read that it's not the best idea but maybe not in this case? – Kawson Apr 15 '21 at 15:19
  • That's... weird. Maybe I'm misunderstanding [cppreference's description](https://en.cppreference.com/w/cpp/language/friend) of what `friend class A;` is doing. It says in 3) that the name of the class doesn't need to be previously declared. – Nathan Pierson Apr 15 '21 at 15:20
  • 2
    @Kawson Including header files in source files is fine and expected. What you've probably read is advice not to include the other way around--including CPP files in H files is often incorrect and a fast route to linker errors. – Nathan Pierson Apr 15 '21 at 15:21
  • Oh. What it's saying is that `friend class A;` is sort of performing a very limited forward declaration, where it's sort of only declared for the purpose of establishing friendship. You still need regular forward declaration for the pointer member variables and function signatures. Weird. – Nathan Pierson Apr 15 '21 at 15:24
  • @NathanPierson Oh, maybe yes. Now it works fine. Thanks for help and this post [link](https://stackoverflow.com/questions/625799/resolve-build-errors-due-to-circular-dependency-amongst-classes) looks be helpful (circular dependancy). – Kawson Apr 15 '21 at 15:24
1

I Assume that your .cpp file includes first SPN.h (before including A.h and B.h, if at all).

What happens during preprocessor stage is this: The preprocessor first replaces the include "SPN.h" statement, with the whole content of SPN.h.

Then it goes over it, and finds #include "A.h" and #include "B.h". So, it replaces the statement #include "A.h" with the contents of A.h.

Then it goes over it, and finds #include "SPN.h", but SPN.h was already added once during this compilation process, so it ignores the #include "SPN.h" statement due to the #prgama once directive.

The same thing happens with B.h.

So, after the preprocessor has completed its work, the include "SPN.h" is replaced by:

class A 
{
public:
    SPN temp;
    void operator()(SPN*& spn);
};

class B 
{
public:
    SPN temp;
    void operator()(SPN*& spn);
};

// the contents of <random>
// the contents of <iostream>

class SPN
{
public:
    friend class A;
    friend class B;
    A* a;
    B* b;
    void operator=(A*& R);
    void operator=(B*& R);
};

And then when the compiler goes over this code, when it analyzes A and B classes, class SPN was not yet declared.

Solution:

Since class SPN only need to recognize that there are classes A and B, but does not need to know their definition (since it only uses pointers to them, and never uses them by-value), you don't have to include A.h and B.h in the SPN.h header, just declaring class A; class B; should be enough. Then, in the .cpp file you can include A.h and B.h and you will have everything you need.

Orielno
  • 409
  • 2
  • 8