Friendship is not supposed to be transitive. That is, my friends cannot access each other's privates (unless they're also friends).
However, with the following function template that is defined inside a befriending class template, this seems to be the case:
template <typename T>
class A {
template <typename>
friend class A; // all other A instances are my friend
int i{}; // some private member
template <typename U>
friend int test(A at, A<U> au) { // int test(A<T>, A<U>) is my friend
return at.i + au.i;
}
};
int main() {
A<int> ai;
A<char> ac;
test(ai,ac);
}
I stumbled over this in a setting where test was called operator+ but the principle is the same as in this minimal example.
The way I see it A<int> befriends A<char> as well as a free function int test(A<int>, A<char>). Which means that those can access A<int>::i. However, A<char> in turn befriends some hypothetical function int test(A<char>, A<int>) but I don't see it befriending int test(A<int>, A<char>) anywhere. Crucially, test is not a member function of any A, despite being defined inside A's body.
Yet, int test(A<int>, A<char>) can access both A<int>::i and A<char>::i, despite both being private.
Both gcc and clang seem to accept this. Which means that I misunderstood something. Or both compilers are in error, which is probably not the case.
Note that they both stop accepting the code if one removes the friendship with other A instances unless one also moves the function template definition past the class template.
What friendship rules dictate that this example is legal? I'm assuming C++23.
This is CWG1699.
[class.access.base]/5.2 says:
A member
mis accessible at the point R when named in class N if:
mas a member of N is private, and R occurs in a direct member or friend of class N
but it is not clear whether an access from the body of a friend function defined within a class C is considered to 'occur in' C for the purpose of this definition.
From the issue:
There are two lines of analysis that lead to opposite conclusions. The first is that a friend defined within the member-specification is written by the class author and is effectively part of the class, not subject to hijacking by other declarations, and thus should be afforded the same access as all other declarations that are part of the class. The second is that giving different access to a friend function based simply on whether it was defined inside or outside of its befriending class is confusing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With