Why does the first example compile?
In your first example, you have a forward declaration of class B
followed by a declaration of a friend function that uses it in its parameter list:
class B;
class A {
...
friend int add(A, B);
};
This is allowed because, although B
is incomplete, we are not defining add
yet, only declaring an intention to eventually do so.
Why does the second example not compile?
In the second example, we have a forward declaration of class Apple
, followed by a definition of showA
:
class Apple;
class B {
...
void showA(Apple d)
{
...
}
};
This time, since we are defining the function, the compiler is obligated to generate code for it. But because Apple
is incomplete, the compiler cannot know, for example, how much space in memory to reserve to hold the parameter d
. Therefore this is an error.
The question When can I use a forward declaration? explains some of what can and cannot be done with an incomplete (forward-declared) type.
The use of 'friend' is irrelevant here
The friend
keyword is basically irrelevant here. friend
primarily affects access control (in the sense of public
and private
), but that's not the issue here.
A detail: friend
also affects scoping. Because of friend
, add
is not a member of class A
, but rather refers to a member of the global scope without actually introducing one (it's weird). But that does not change whether an incomplete type can be used as a parameter.