This happens due to injected-class-name, unqualified name lookup rules and the fact the name lookup is completed before check for accessibility.
injected-class-name is a mechanism that makes class name available inside that class definition.
Now, the unqualified name lookup rules within a class definition state that first the scope of the class is searched, then the scopes of any base classes are searched recursively, and only after that (and some more steps) you perform normal search in namespace scope.
Putting this all together:
- There are 2 names
MyInterfacein scope ofInherited– one as injected-class-name and one that resides in the same namespace asInherited(the global namespace). - Name lookup in
Inheritedfirst findsMyInterfaceas injected-class-name inherited fromMyImpl. Name lookup is satisfied and doesn’t search any longer for other instances of the name. - However, the name
MyInterfaceinherited fromMyImplis not accessible toInherited, because there isprivateinheritance – an error happens.
The way to fix that is to change unqualified name lookup into qualified one:
struct Inherited : public MyImpl {
void doSomething(::MyInterface* mi) {}
};
Now, injected-class-name cannot satisfy name lookup, because you explicitly ask for MyInterface from global namespace, not any MyInterface that happens to match. And since the name MyInterface in global namespace is public (like all namespace names), it can used without any issue.