This appears to be a discrepancy in the language wording, with different compilers taking different sides on the issue. MSVC and clang will accept the code as-is, but compilers like GCC and Edge reject it.
The conflicting wording comes from:
10.3.1.2 [namespace.memdef]
... the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.
The struct Baz
is not declared in the innermost enclosing namespace, but it is visible there, so normal name lookup would find it. But since this isn't normal name lookup, Compilers like gcc and Edge don't look into the enclosing namespaces, only the innermost.
This information is from this filed gcc bug which discusses the topic.
It appears that MSVC and Edge choose to interpret using anonymous namespaces differently, which would transform OP's code to the following:
namespace unnamed { }
using namespace unnamed;
namespace unnamed { struct Baz; }
class Foo { protected: int x; };
class Bar : public Foo { friend class Baz; };
namespace unnamed { class Baz { void f() { Bar b; b.x = 42; } }; }
This equivalent code is also rejected by compilers like gcc and Edge, but accepted by MSVC and clang due to a different interpretation of whether types that are brought in via using
declarations or directives are considered for friend
name lookup. More can be seen on this issue at cwg-138