I'm looking at the following documentation
https://en.cppreference.com/w/cpp/language/friend
The first Syntax example (friend function-declaration) has me confused
It says:
class Y
{
int data; // private member
// the non-member function operator<< will have access to Y's private members
friend std::ostream& operator<<(std::ostream& out, const Y& o);
friend char* X::foo(int); // members of other classes can be friends too
friend X::X(char), X::~X(); // constructors and destructors can be friends
};
// friend declaration does not declare a member function
// this operator<< still needs to be defined, as a non-member
std::ostream& operator<<(std::ostream& out, const Y& y)
{
return out << y.data; // can access private member Y::data
}
my confusion comes with the following line of code friend char* X::foo(int); // members of other classes can be friends too
. I don't know why would we want to declare such a function since I don't see any use to giving friendship to a member function of another class ( class X
in this case) since we are not passing a class Y
reference object as an argument, so we can't really access Y
's private member via the foo
function.
Is there an use case where this example could be useful ?
I would like to start by noting that not everything permitted by the C++ standard is necessarily useful. Sometimes things are allowed because they do no harm and forbidding them would make the standard more complicated. In these cases, the standard tends to prefer simplicity, which implies being permissive.
Sometimes, this permissiveness is itself useful because eventually someone finds a use for the allowed thing that had appeared useless. So inquiring about usefulness can be productive. Questions like this one are good for the growth of knowledge, as long as one does not get too hung up on them. Sometimes examples are just examples of what is possible, not of what is (known to be) useful.
Now on to the case at hand.
Let's suppose that X
has a member of type Y
. It could be reasonable to give X
access to the data
of Y
. For example, the definition of X
might look like the following. (The use of unique_ptr
is to avoid a circular dependency problem – the definition of X
needs to occur before the definition of Y
.)
class Y; // Forward declaration
class X
{
std::unique_ptr<Y> token;
public:
X(char);
~X();
char* foo(int);
Y getToken();
void giveToken(Y);
};
In this setup, with friendship, the constructor would be able to set token->data
to whatever value is desired, foo()
would be able to change that value, and the destructor could read that value.
While this much gives a reason for the friend
declarations, so far replacing Y
with a private nested class would do as well. That's why I added the getToken()
and giveToken()
members. I could see this class giving away copies of *token
for consumers to hold. Think of this as taking a snapshot. Since the consumers are not friends of Y
, the token is effectively immutable to them. They can copy it, move it, discard it, but not modify it. (If the Y
constructor was made private, consumers also would be unable to create new tokens.)
Later, the token could be given to an object – not necessarily the same object, not necessarily an X
object – perhaps to reset its internal state to the snapshot. (This can be done without friendship if it can be accomplished via *pimpl = y;
.) If there are multiple classes that can process these tokens, there is reason to make Y
an independent class rather than a nested class.
I do not claim this is a good design, but it is a valid one.