In a class template A, a different instance of A is befriended. Specifically A<T> and A<T const> befriend each other (see below). This should not mean that friends of, say, A<T> should be able to access private members of A<T const> or vice versa. But apparently gcc and clang accept this.
#include <type_traits>
template <typename T>
struct A {
private:
T value{};
public:
using InvT = std::conditional_t<std::is_const_v<T>, std::remove_const_t<T>, std::add_const_t<T>>;
friend A<InvT>;
friend bool operator==(A const& a, A<InvT> const& b) noexcept {
return a.value == b.value;
// ^^^^^ private. should not be accessible
}
};
Are they just wrong to accept this?
This is CWG 1699: if a friend function is defined lexically within the class definition, does the friend get access to everything that the class has access to? Clang and GCC currently consider the answer to be "yes". MSVC considers the answer to be "no". All implementations agree that when the definition of a friend function is outside the class, the body of the function doesn't automatically get access to all names that the class has access to.
In your example, because the
operator==in question is defined inside the definition ofA<const int>, the friend function gets access to everything thatA<const int>has access to, which includes all ofA<int>'s private members sinceA<int>has declaredA<const int>to be its friend.If the friend declaration of
operator==is made into a forward declaration and the definition of thatoperator==is supplied out-of-line, then all three compilers reject the code since friendship is not transitive.If the inline friend definition is kept but the declaration
friend A<InvT>;is removed, then Clang rejects the code but GCC continues to accept it. This is probably a bug in GCC.